问题
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