为什么使用CompletableFuture

  • Future虽然可以实现获取异步执行结果的需求,但是它没有提供通知的机制,我们无法得知Future什么时候完成。
  • 要么使用阻塞,在future.get()的地方等待future返回的结果,这时又变成同步操作。要么使用isDone()轮询地判断Future是否完成,这样会耗费CPU的资源。

CompletableFuture实例

  1. //任务1:洗水壶->烧开水
  2. CompletableFuture<Void> f1 =
  3. CompletableFuture.runAsync(()->{
  4. System.out.println("T1:洗水壶...");
  5. sleep(1, TimeUnit.SECONDS);
  6. System.out.println("T1:烧开水...");
  7. sleep(15, TimeUnit.SECONDS);
  8. });
  9. //任务2:洗茶壶->洗茶杯->拿茶叶
  10. CompletableFuture<String> f2 =
  11. CompletableFuture.supplyAsync(()->{
  12. System.out.println("T2:洗茶壶...");
  13. sleep(1, TimeUnit.SECONDS);
  14. System.out.println("T2:洗茶杯...");
  15. sleep(2, TimeUnit.SECONDS);
  16. System.out.println("T2:拿茶叶...");
  17. sleep(1, TimeUnit.SECONDS);
  18. return "龙井";
  19. });
  20. //任务3:任务1和任务2完成后执行:泡茶
  21. CompletableFuture<String> f3 =
  22. f1.thenCombine(f2, (__, tf)->{
  23. System.out.println("T1:拿到茶叶:" + tf);
  24. System.out.println("T1:泡茶...");
  25. return "上茶:" + tf;
  26. });
  27. //等待任务3执行结果
  28. System.out.println(f3.join());
  29. void sleep(int t, TimeUnit u) {
  30. try {
  31. u.sleep(t);
  32. }catch(InterruptedException e){}
  33. }
  34. // 一次执行结果:
  35. T1:洗水壶...
  36. T2:洗茶壶...
  37. T1:烧开水...
  38. T2:洗茶杯...
  39. T2:拿茶叶...
  40. T1:拿到茶叶:龙井
  41. T1:泡茶...
  42. 上茶:龙井

优点:

  1. 不用手工维护线程,
  2. 对于要求线程执行顺序的场景非常适合。

线程之间的关系(不同的关系使用不同的函数)

  • 串行

image.png

  • 并行

image.png

  • 汇聚(thenCombine)

image.png

异常处理

类比同步编程中的try catch finally

try catch finally
exceptionally whenComplete()(不支持返回结果) 和 handle()

【注】希望返回结果,则选择,supplyAsync或者如果您只想运行异步操作,则选择runAsync

课后思考

创建采购订单的时候,需要校验一些规则,例如最大金额是和采购员级别相关的。有同学利用 CompletableFuture 实现了这个校验的功能,逻辑很简单,首先是从数据库中把相关规则查出来,然后执行规则校验。你觉得他的实现是否有问题呢?

  1. //采购订单
  2. PurchersOrder po;
  3. CompletableFuture<Boolean> cf =
  4. CompletableFuture.supplyAsync(()->{
  5. //在数据库中查询规则
  6. return findRuleByJdbc();
  7. }).thenApply(r -> {
  8. //规则校验
  9. return check(po, r);
  10. });
  11. Boolean isOk = cf.join();

解答

1、异常没有处理
2、查询操作属于IO操作。应该使用线程池,避免线程饥饿