Java8 CompletableFuture
package com.example.cesium.utils; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ForkJoinPool; public class CompletableFutureDemo { public static void main(String[] args) { //doSupplyAsync();//开启一个带返回值的异步任务 --- 小白在打王者和厨师做饭 应该为并行操作 //doThenCompose();//连接两个异步任务 --- 小白在打王者 和 (厨师做饭,然后 服务员打饭)并行操作 //doThenCombine();//合并两个异步任务 --- 小白打王者 厨师炒饭 服务员焖饭 三个并行 //doThenApply();// 任务后置处理(同一个线程) --- 小白接电话,(服务员收款,然后 服务员开发票)并行 //doThenApplyAsync();// 任务后置处理(不同线程) --- 小白接电话,(服务员收款,然后 另一个服务员开发票)并行 //doApplyToEither();//用来获取最先完成的任务 --- 比较700路 和 800路 哪个先到 //doApplyToEither2();// 比较700路 和 800路 哪个先到 , 坐上700路撞树了 //doExceptionally();//对于异常的处理,除了尾部也可以中间加 System.out.println(Runtime.getRuntime().availableProcessors()); System.out.println(ForkJoinPool.commonPool().getPoolSize()); System.out.println(ForkJoinPool.getCommonPoolParallelism()); } private static void doExceptionally(){ SmallTool.printTimeAndThread("小白走出餐厅 , 来到公共汽车站"); SmallTool.printTimeAndThread("等待 700路 或者 800路 公交车到来"); CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> { SmallTool.printTimeAndThread("700路公交正在赶来"); SmallTool.sleepMillis(100); return "700路到了"; }).applyToEither(CompletableFuture.supplyAsync(() -> { SmallTool.printTimeAndThread("800路公交正在赶来"); SmallTool.sleepMillis(200); return "800路到了"; }), firstBus -> { SmallTool.printTimeAndThread(firstBus); if(firstBus.startsWith("700")){ throw new RuntimeException("撞树了。。。"); } return firstBus; }).exceptionally(e->{ SmallTool.printTimeAndThread(e.getMessage()); SmallTool.printTimeAndThread("小白叫出租车"); return "出租车叫到了"; }); SmallTool.printTimeAndThread(String.format("%s,小白坐车回家",future.join())); } private static void doApplyToEither2(){ SmallTool.printTimeAndThread("小白走出餐厅 , 来到公共汽车站"); SmallTool.printTimeAndThread("等待 700路 或者 800路 公交车到来"); CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> { SmallTool.printTimeAndThread("700路公交正在赶来"); SmallTool.sleepMillis(100); return "700路到了"; }).applyToEither(CompletableFuture.supplyAsync(() -> { SmallTool.printTimeAndThread("800路公交正在赶来"); SmallTool.sleepMillis(200); return "800路到了"; }), firstBus -> { SmallTool.printTimeAndThread(firstBus); if(firstBus.startsWith("700")){ throw new RuntimeException("撞树了。。。"); } return firstBus; }); SmallTool.printTimeAndThread(String.format("%s,小白坐车回家",future.join())); } private static void doApplyToEither(){ SmallTool.printTimeAndThread("小白走出餐厅 , 来到公共汽车站"); SmallTool.printTimeAndThread("等待 700路 或者 800路 公交车到来"); CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> { SmallTool.printTimeAndThread("700路公交正在赶来"); SmallTool.sleepMillis(100); return "700路到了"; }).applyToEither(CompletableFuture.supplyAsync(() -> { SmallTool.printTimeAndThread("800路公交正在赶来"); SmallTool.sleepMillis(200); return "800路到了"; }), (firstBus -> firstBus)); SmallTool.printTimeAndThread(String.format("%s,小白坐车回家",future.join())); } private static void doThenApplyAsync(){ SmallTool.printTimeAndThread("小白吃好了"); SmallTool.printTimeAndThread("小白 结账、要求开发票"); CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> { SmallTool.printTimeAndThread("服务员收款 500元"); SmallTool.sleepMillis(100); return "500"; }).thenApplyAsync(money -> { SmallTool.printTimeAndThread(String.format("服务员开发票, 面额 %s元",money)); SmallTool.sleepMillis(200); return String.format("%s元发票",money); }); SmallTool.printTimeAndThread("小白 接到朋友电话,想一起打游戏"); SmallTool.printTimeAndThread(String.format("小白拿到%s,准备回家",future.join())); } private static void doThenApply(){ SmallTool.printTimeAndThread("小白吃好了"); SmallTool.printTimeAndThread("小白 结账、要求开发票"); CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> { SmallTool.printTimeAndThread("服务员收款 500元"); SmallTool.sleepMillis(100); return "500"; }).thenApply(money -> { SmallTool.printTimeAndThread(String.format("服务员开发票, 面额 %s元",money)); SmallTool.sleepMillis(200); return String.format("%s元发票",money); }); SmallTool.printTimeAndThread("小白 接到朋友电话,想一起打游戏"); SmallTool.printTimeAndThread(String.format("小白拿到%s,准备回家",future.join())); } private static void doThenCombine(){ SmallTool.printTimeAndThread("小白进入餐厅"); SmallTool.printTimeAndThread("小白点了 番茄炒蛋 + 一碗米饭"); CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> { SmallTool.printTimeAndThread("厨师炒菜"); SmallTool.sleepMillis(200); return "番茄炒蛋"; }).thenCombine(CompletableFuture.supplyAsync(() -> { SmallTool.printTimeAndThread("服务员焖米饭"); SmallTool.sleepMillis(300); return "米饭"; }), (dish, rice) -> { SmallTool.printTimeAndThread("服务员打饭"); SmallTool.sleepMillis(100); return String.format("%s + %s 好了", dish, rice); }); SmallTool.printTimeAndThread("小白在打王者"); SmallTool.printTimeAndThread(String.format("%s, 小白开吃",future.join())); } private static void doThenCompose(){ SmallTool.printTimeAndThread("小白进入餐厅"); SmallTool.printTimeAndThread("小白点了 番茄炒蛋 + 米饭"); CompletableFuture<Object> future = CompletableFuture.supplyAsync(() -> { SmallTool.printTimeAndThread("厨师炒菜"); SmallTool.sleepMillis(200); return "番茄炒蛋"; }).thenCompose(dish -> CompletableFuture.supplyAsync(()->{ SmallTool.printTimeAndThread("服务员打饭"); SmallTool.sleepMillis(100); return dish + "米饭"; })); SmallTool.printTimeAndThread("小白在打王者"); SmallTool.printTimeAndThread(String.format("%s,小白开吃",future.join())); } /** * 1.小白进入餐厅 * 2.小白点了 番茄炒饭 + 一碗米饭 * 3.小白在打王者 * 4.厨师炒菜 200ms * 5.厨师打饭 100ms * 6.小白开吃 */ private static void doSupplyAsync(){ SmallTool.printTimeAndThread("小白进入餐厅"); SmallTool.printTimeAndThread("小白点了 番茄炒饭 + 一碗米饭"); CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> { SmallTool.printTimeAndThread("厨师炒菜"); SmallTool.sleepMillis(200); SmallTool.printTimeAndThread("厨师打饭"); SmallTool.sleepMillis(100); return "番茄炒蛋 + 米饭 做好了"; }); SmallTool.printTimeAndThread("小白在打王者"); SmallTool.printTimeAndThread(String.format("%s , 小白开吃",future.join())); } // // // }
常用API
任务执行
supplyAsync:有返回值
runAsync:无返回值
任务接力
thenRun thenRunAsync
thenAccept thenAcceptAsync
thenApply thenApplyAsync
handle handleAsync
applyToEither applyToEitherAsync
acceptEither acceptEitherAsync
runAfterEither runAfterEitherAsync
thenCombine thenCombineAsync
thenAcceptBoth thenAcceptBothAsync
说明
带run的方法,无入参,无返回值。
带accept的方法,有入参,无返回值。
带supply的方法,无入参,有返回值。
带apply的方法,有入参,有返回值。
带handle的方法,有入参,有返回值,并且带异常处理。
以Async结尾的方法,都是异步的,否则是同步的。
以Either结尾的方法,只需完成任意一个。
以Both/Combine结尾的方法,必须所有都完成。
任务结果获取
join 阻塞等待,不会抛异常
get 阻塞等待,会抛异常
complete(T value) 不阻塞,如果任务已完成,返回处理结果。如果没完成,则返回传参value。
completeExceptionally(Throwable ex) 不阻塞,如果任务已完成,返回处理结果。如果没完成,抛异常。
基于以上说明,再举一个在实际工作中遇到的问题以及解决。
场景:导出所有点、线、面 有序 数据并封装json,速度慢 1-2秒。
思路:使用CompletableFuture的异步跑点线面三个不同线程,并把三个线程组成一个task,监听task中的子任务,执行完成后封装到结果集
实现:
public JSONObject getAllGeoJson(Long miningId) throws IOException { JSONArray features = new JSONArray(); JSONObject geoJSON = new JSONObject(); geoJSON.put("type", "FeatureCollection"); CompletableFuture<JSONArray> roadTask = CompletableFuture.supplyAsync(() -> { return roadService.findAllInfo2(miningId); }).exceptionally(e -> { e.printStackTrace(); return new JSONArray(); }); CompletableFuture<JSONArray> zoneTask = CompletableFuture.supplyAsync(() -> { return workzoneService.findAllInfo2(miningId); }).exceptionally(e -> { e.printStackTrace(); return new JSONArray(); }); CompletableFuture<JSONArray> pointTask = CompletableFuture.supplyAsync(() -> { try { return workPointService.findAllInfo(miningId); } catch (IOException e) { e.printStackTrace(); return new JSONArray(); } }).exceptionally(e -> { return new JSONArray(); }); CompletableFuture<Void> allFirstTasks = CompletableFuture.allOf(pointTask, roadTask, zoneTask); allFirstTasks.thenRun(() -> { features.add(pointTask.join()); features.add(roadTask.join()); features.add(zoneTask.join()); }).join(); geoJSON.put("features", features); return geoJSON; }