Java 8 CompletableFuture , Stream and Timeouts

匿名 (未验证) 提交于 2019-12-03 02:23:02

问题:

i'm trying to process some amount of data concurrently using CompletableFuture and Stream So far i have:

public static void main(String[] args) throws InterruptedException, ExecutionException {     System.out.println("start");      List<String> collect = Stream.of("1", "2", "3", "4", "5",             "6", "7")             .map(x -> CompletableFuture.supplyAsync(getStringSupplier(x)))             .collect(Collectors.toList())             .stream()             .map(CompletableFuture::join)             .collect(Collectors.toList());     System.out.println("stop out!"); }   public static Supplier<String> getStringSupplier(String text) {     return () -> {          System.out.println("start " + text);         try {             TimeUnit.SECONDS.sleep(2);         } catch (InterruptedException e) {             e.printStackTrace();         }         System.out.println("stop " + text);         return "asd" + text;     }; } 

And output is fine :

start start 1 start 4 start 3 start 2 start 5 start 6 start 7 stop 4 stop 1 stop 5 stop 2 stop 6 stop 3 stop 7 stop out!

However right now i want to add timeout to that job. Lets say it should be canceled after 1 SECOND. And return null or some other value to collect list. (I would prefer some value indicating cause).

How can i achieve that ?

Thanks for help in advance.

回答1:

I have found the way of doing that:

 private static final ScheduledExecutorService scheduler =         Executors.newScheduledThreadPool(                 1,                 new ThreadFactoryBuilder()                         .setDaemon(true)                         .setNameFormat("failAfter-%d")                         .build());  public static void main(String[] args) throws InterruptedException, ExecutionException {     System.out.println("start");     final CompletableFuture<Object> oneSecondTimeout = failAfter(Duration.ofSeconds(1))             .exceptionally(xxx -> "timeout exception");     List<Object> collect = Stream.of("1", "2", "3", "4", "5", "6", "7")             .map(x -> CompletableFuture.anyOf(createTaskSupplier(x)                     , oneSecondTimeout))             .collect(Collectors.toList())             .stream()             .map(CompletableFuture::join)             .collect(Collectors.toList());     System.out.println("stop out!");     System.out.println(collect); }  public static CompletableFuture<String> createTaskSupplier(String x) {     return CompletableFuture.supplyAsync(getStringSupplier(x))             .exceptionally(xx -> "PROCESSING ERROR : " + xx.getMessage()); }   public static Supplier<String> getStringSupplier(String text) {     return () -> {          System.out.println("start " + text);         try {             TimeUnit.MILLISECONDS.sleep(100);         } catch (InterruptedException e) {             e.printStackTrace();         }         if (text.equals("1")) {             throw new RuntimeException("LOGIC ERROR");         }         try {             if (text.equals("7"))                 TimeUnit.SECONDS.sleep(2);         } catch (InterruptedException e) {             e.printStackTrace();         }         System.out.println("stop " + text);         return "result " + text;     }; }  public static <T> CompletableFuture<T> failAfter(Duration duration) {     final CompletableFuture<T> promise = new CompletableFuture<>();     scheduler.schedule(() -> {         final TimeoutException ex = new TimeoutException("Timeout after " + duration);         return promise.completeExceptionally(ex);     }, duration.toMillis(), MILLISECONDS);     return promise; } 

It returns :

 start  start 1  start 3  start 4  start 2  start 5  start 6  start 7  stop 6  stop 4  stop 3  stop 5  stop 2  stop out!  [PROCESSING ERROR : java.lang.RuntimeException: LOGIC ERROR, result 2, result 3, result 4, result 5, result 6, timeout exception]` 

What do you think about that, can you spot any flaws of that solution ?



回答2:

For others, who are not limited with Java 8, you can use completeOnTimeout method, which was introduced in Java 9.

List<String> collect = Stream.of("1", "2", "3", "4", "5", "6", "7")         .map(x -> CompletableFuture.supplyAsync(getStringSupplier(x))                 .completeOnTimeout(null , 1, SECONDS))         .filter(Objects::nonNull)         .collect(toList())         .stream()         .map(CompletableFuture::join)         .collect(toList()); 


回答3:

You can wrap the job in another CompletableFuture and it would give out a TimeoutException if the given time is exceeded. You can separate the TimeoutException catch block if you want to handle it specially.

    List<String> collect = null;     try {         collect = CompletableFuture.supplyAsync(() ->                 Stream.of("1", "2", "3", "4", "5",                         "6", "7")                         .map(x -> CompletableFuture.supplyAsync(getStringSupplier(x)))                         .collect(Collectors.toList())                         .stream()                         .map(CompletableFuture::join)                         .collect(Collectors.toList())         ).get(5, TimeUnit.SECONDS);     } catch (InterruptedException | ExecutionException | TimeoutException e) {         e.printStackTrace();         //separate out the TimeoutException if you want to handle it differently       }      System.out.println(collect); //would be null in case of any exception 


回答4:

you can try CompletableFuture's overloaded supplyAsync method with executor parameter (CompletableFuture.supplyAsync(getStringSupplier(x), timeoutExecutorService)) and can refer link for timeoutExecutorService.



易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!