首页 > 编程语言 >java Future多个任务的超时时间问题

java Future多个任务的超时时间问题

时间:2024-01-23 18:23:33浏览次数:20  
标签:now 01 java System 2024 Future println 超时 out

问题

    public static void main(String[] args) throws InterruptedException, ExecutionException, TimeoutException {
        ExecutorService executor = Executors.newSingleThreadExecutor();
        Future<String> future = executor.submit(() -> "123");
        String s = future.get(1, TimeUnit.SECONDS);
        System.out.println(s);
    }

  上图是一段简单代码,表示最多等待一秒钟获取任务执行结果,否则超时,但这个超时时间是从什么时候开始计算的呢?

public static void main(String[] args) throws InterruptedException, ExecutionException {
        ExecutorService executor = Executors.newSingleThreadExecutor();
        System.out.println(LocalDateTime.now());
        Future<String> future = executor.submit(() -> {
            TimeUnit.MINUTES.sleep(1);
            return "123";
        });
        TimeUnit.SECONDS.sleep(3);
        System.out.println(LocalDateTime.now());
        String s = null;
        try {
            s = future.get(1, TimeUnit.SECONDS);
        } catch (TimeoutException e) {
            System.out.println(LocalDateTime.now());
        }
    }
2024-01-23T16:11:26.388319
2024-01-23T16:11:29.392559
2024-01-23T16:11:30.394918

  通过这个示例,可以看到这个超时时间并不是从submit这个任务的时候开始的,而是在get的时候开始计算等待时间,如果有多个任务的时候,程序并不能像我以为的那样运行。例如如下

public static void main(String[] args) throws InterruptedException, ExecutionException {
        ExecutorService executor = Executors.newSingleThreadExecutor();
        System.out.println(LocalDateTime.now());
        
        List<Future<String>> futures = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            Future<String> future = executor.submit(() -> {
                TimeUnit.MINUTES.sleep(1);
                return "123";
            });
            futures.add(future);
        }
        for (Future<String> future : futures) {
            try {
                String s = future.get(1, TimeUnit.SECONDS);
            } catch (TimeoutException e) {
                System.out.println(LocalDateTime.now());
            }
        }
    }
2024-01-23T16:21:34.607510
2024-01-23T16:21:35.611482
2024-01-23T16:21:36.611936
2024-01-23T16:21:37.612215
2024-01-23T16:21:38.613503
2024-01-23T16:21:39.613871

  我预期的是添加5个任务,然后最多等待1秒返回,但因为超时时间是按照get的时间开始,所以整个流程足足等待了5秒

 

解决方案

  方案1 使用invokeAll方法

public static void main(String[] args) throws InterruptedException, ExecutionException {
        ExecutorService executor = Executors.newFixedThreadPool(1024);
        System.out.println(LocalDateTime.now());

        List<Callable<String>> callables = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            callables.add(()->{
                TimeUnit.MINUTES.sleep(1);
                return "123";
            });
        }
        System.out.println(LocalDateTime.now());
        List<Future<String>> futures = executor.invokeAll(callables,3,TimeUnit.SECONDS);
        for (Future<String> future : futures) {
            try {
                String s = future.get();
            } catch (Exception e){
                System.out.println("超时"+LocalDateTime.now());
            }
        }

    }
2024-01-23T16:35:14.700841
2024-01-23T16:35:14.702355
超时2024-01-23T16:35:17.704613
超时2024-01-23T16:35:17.707435
超时2024-01-23T16:35:17.707512
超时2024-01-23T16:35:17.707588
超时2024-01-23T16:35:17.707655

  可以看到使用线程池的invoke方法后,所有任务都在3秒后快速超时。

  方法优点:简单粗暴,能符合大部分时候的需求

  方法缺点:对多个任务返回值不同时不太友好;多个任务无法独立设置超时时间

 

  方案2 使用CompletableFuture.allOf()

    public static void main(String[] args) throws InterruptedException {
        System.out.println(LocalDateTime.now());
        CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
            }
            return "123";
        });
        CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> {
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
            }
            return 123;
        });

        System.out.println(LocalDateTime.now());
        try {
            CompletableFuture.allOf(future1,future2).get(1,TimeUnit.SECONDS);
        }  catch (ExecutionException | TimeoutException e) {
        }
        if (future1.isDone()){
            try {
                future1.get();
                System.out.println(LocalDateTime.now());
            } catch (ExecutionException e) {
            }
        }else {
            System.out.println(LocalDateTime.now());
        }
        if (future2.isDone()){
            try {
                future2.get();
                System.out.println(LocalDateTime.now());
            } catch (ExecutionException e) {
            }
        }else {
            System.out.println(LocalDateTime.now());
        }
    }
2024-01-23T17:42:39.389196
2024-01-23T17:42:39.393157
2024-01-23T17:42:40.394024
2024-01-23T17:42:40.394156

  方法优点:简单粗暴,能符合大部分时候的需求

  方法缺点:多个任务无法独立设置超时时间

  如果用的java8以下可以用CountDownLatch代替,在各个任务的finaly里面countDown,将await的等待时间作为超时时间即可。

 

  方案3 使用CompletableFuture.orTimeout

public static void main(String[] args) throws InterruptedException {
        System.out.println(LocalDateTime.now());
        CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
            try {
                TimeUnit.MINUTES.sleep(1);
            } catch (InterruptedException e) {
            }
            return "123";
        }).orTimeout(1,TimeUnit.SECONDS);
        CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> {
            try {
                TimeUnit.MINUTES.sleep(1);
            } catch (InterruptedException e) {
            }
            return 123;
        }).orTimeout(2,TimeUnit.SECONDS);

        System.out.println(LocalDateTime.now());
        try {
            future1.get();
        }  catch (ExecutionException e) {
            System.out.println(LocalDateTime.now());
        }
        try {
            future2.get();
        }  catch (ExecutionException e) {
            System.out.println(LocalDateTime.now());
        }
    }
2024-01-23T17:45:27.526374
2024-01-23T17:45:27.533786
2024-01-23T17:45:28.535025
2024-01-23T17:45:29.534531

  方法优点:基本完美符合各种需求

  方法缺点:该方法从JAVA9开始提供(手动狗头) 

标签:now,01,java,System,2024,Future,println,超时,out
From: https://www.cnblogs.com/hetutu-5238/p/17982836

相关文章

  • java 反射获取某个类的属性名和属性的值
    /***根据字段饰扣可以为空获取每个属性的变更内容如课程名称:英语-->数学;*@paramvo*@paramfieldName字段名:传值类型为CourseName周首字母大写*@paramdescribe字段的描述:可以理解为就是字段的名称如课程名称*@paramsbStringBuffer对象*@return*@th......
  • elasticsearchjava客户端
    elasticsearchjava客户端1.引用maven配置<dependency><groupId>org.elasticsearch.client</groupId><artifactId>elasticsearch-rest-high-level-client</artifactId><exclusions><exclusion>&l......
  • Java resultset判断mysql表是否存在
    importjava.sql.*;publicclassCheckTableExistence{publicstaticvoidmain(String[]args)throwsSQLException{Stringurl="jdbc:mysql://localhost:3306/mydatabase";//MySQL服务器地址及数据库名称Stringusername="root"......
  • Java反编译工具 JD-GUI安装使用
    将源代码转换成二进制执行代码的过程叫“编译”,那么反编译就是将二进制执行代码转换成源代码。在java开发里,源代码是.java文件,然后经过编译后生成计算机识别的.class文件,但是.class文件是计算机识别的我们一般看不明白,因此需要反编译变成我们能读懂的源码,但是反编译后的......
  • 生辰八字算五行java实现
    importorg.junit.runner.RunWith;importorg.springframework.boot.test.context.SpringBootTest;importorg.springframework.test.context.junit4.SpringJUnit4ClassRunner;@RunWith(SpringJUnit4ClassRunner.class)@SpringBootTestpublicclassWuXingAndEightTest{p......
  • Java垃圾回收机制:理解与实践
    Java语言的一个显著特点是其自动内存管理,即垃圾回收(GarbageCollection,GC)。GC可以自动监控每个对象的引用情况,当一个对象不再被引用时,GC会自动释放该对象占用的内存。这大大简化了开发者的内存管理工作,但也带来了性能上的挑战。本文将探讨Java中的垃圾回收机制,并通过代码示例解释......
  • 写给不耐烦程序员的 JavaScript 指南(二)
    第四部分:原始值原文:exploringjs.com/impatient-js/pt_primitive-values.html译者:飞龙协议:CCBY-NC-SA4.0下一步:14非值undefined和null十四、非值的undefined和null原文:exploringjs.com/impatient-js/ch_undefined-null.html译者:飞龙协议:CCBY-NC-SA4.014......
  • Java web的过滤器Filter
    注:来自《JavaWeb入门经典》一书,仅供参考和学习。1.过滤器的核心对象2.创建并配置过滤器......
  • 深入 JavaScript:理论和技术(上)
    第一部分:前言原文:exploringjs.com/deep-js/pt_frontmatter.html译者:飞龙协议:CCBY-NC-SA4.0下一步:1关于本书一、关于这本书原文:exploringjs.com/deep-js/ch_about-book.html译者:飞龙协议:CCBY-NC-SA4.01.1 这本书的主页在哪里?1.2 这本书包括什么?1.3 ......
  • IBM java的分析工具(ga和ha)学习和整理
    IBMjava的分析工具(ga和ha)学习和整理背景前几天学习了整理了jca工具今天继续学习一下ga工具ga工具主要是分析gclog相关.可以很直观的进行gclog的分析和展示.除了mat之外还有一个比较轻量级的内存dump分析工具ha.想着一起学习和分析一下.ga工具的相关学习下载......