使用jdk8中的java.util.concurrent.CompletableFuture非常方便进行异步任务编排
1.supplyAsync 开启异步任务
/**
* 1.小白点菜
* 2.厨师做菜
* 3.小白吃饭
*/
public static void test1() {
SmallTools.printTimeAndThread("小白进入餐厅");
SmallTools.printTimeAndThread("小白点了 番茄炒蛋+一碗米饭");
//启动一个线程执行厨师做饭
CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
SmallTools.printTimeAndThread("厨师炒菜");
SmallTools.sleepMills(200);
SmallTools.printTimeAndThread("厨师打饭");
return "番茄炒蛋+一碗米饭 已经好了";
});
SmallTools.printTimeAndThread("小白在打王者");
SmallTools.printTimeAndThread("小白开吃," + completableFuture.join());
}
2.主动计算
getNow有点特殊,如果结果已经计算完则返回结果或者抛出异常,否则返回给定的valueIfAbsent值。不管线程任务是在运行还是结束了,如果在运行直接终止掉,只要执行了getNow后就会结束掉线程任务,如果没有获取到值就会使用默认值
join()与get()区别在于join()返回计算的结果或者抛出一个unchecked异常(CompletionException),而get()返回一个具体的异常.他们都会阻塞主线程,等待异步任务线程执行完。
get(): 会阻塞等待线程任务执行完毕,并且获取到返回值。 异常是checked异常,必须进行处理
public static void test2() {
SmallTools.printTimeAndThread("小白进入餐厅");
SmallTools.printTimeAndThread("小白点了 番茄炒蛋+一碗米饭");
//启动一个线程执行厨师做饭
CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
SmallTools.sleepMills(200);
SmallTools.printTimeAndThread("厨师炒菜");
SmallTools.sleepMills(200);
SmallTools.printTimeAndThread("厨师打饭");
if(true){
throw new RuntimeException("抛出异常测试");
}
return "番茄炒蛋+白米饭";
});
SmallTools.printTimeAndThread("小白在打王者");
//getNow不管线程任务是在运行还是结束了,如果在运行直接终止掉,只要执行了getNow后就会结束掉线程任务,如果没有获取到值就会使用默认值
try {
SmallTools.printTimeAndThread("小白开吃," + completableFuture.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
join():会阻塞等待线程任务执行完毕,并且获取到返回值。 异常是unchecked异常
public static void test2() {
SmallTools.printTimeAndThread("小白进入餐厅");
SmallTools.printTimeAndThread("小白点了 番茄炒蛋+一碗米饭");
//启动一个线程执行厨师做饭
CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
SmallTools.sleepMills(200);
SmallTools.printTimeAndThread("厨师炒菜");
SmallTools.sleepMills(200);
SmallTools.printTimeAndThread("厨师打饭");
if(true){
throw new RuntimeException("抛出异常测试");
}
return "番茄炒蛋+白米饭";
});
SmallTools.printTimeAndThread("小白在打王者");
SmallTools.printTimeAndThread("小白开吃," + completableFuture.join());
}
getNow()
public static void test1() {
SmallTools.printTimeAndThread("小白进入餐厅");
SmallTools.printTimeAndThread("小白点了 番茄炒蛋+一碗米饭");
//启动一个线程执行厨师做饭
CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
SmallTools.sleepMills(200);
SmallTools.printTimeAndThread("厨师炒菜");
SmallTools.sleepMills(200);
SmallTools.printTimeAndThread("厨师打饭");
return "番茄炒蛋+白米饭";
});
SmallTools.printTimeAndThread("小白在打王者");
//getNow不管线程任务是在运行还是结束了,如果在运行直接终止掉,只要执行了getNow后就会结束掉线程任务,如果没有获取到值就会使用默认值
SmallTools.printTimeAndThread("小白开吃," + completableFuture.getNow("默认值"));
}
如果使用 CompletableFuture.supplyAsync() 开启异步任务后,没有使用getNow join get方法,那么只要主线程结束,不管什么异步任务线程是否执行完,都不会等待他,会直接终止掉,同时主线程也执行结束
3.thenCompose
连接两个有依赖关系的异步任务,第一个线程执行完后会有一个返回值,然后传给第二个线程任务,第二个线程任务在开始执行
/**
* 1.小白点菜
* 2.厨师炒菜
* 小白打游戏
* 3.等待厨师完成后,服务员在打饭
* 4.小白开始吃
*/
public static void test2() {
SmallTools.printTimeAndThread("小白进入餐厅");
SmallTools.printTimeAndThread("小白点了 番茄炒蛋+一碗米饭");
//thenCompose的作用是 等待前一个任务结束后,有结果,下一个任务才会出发,
//在这里就是必须要等待厨师炒菜完成后,服务员此才会打饭,服务员和厨师是不同的线程
CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
SmallTools.printTimeAndThread("厨师炒菜");
SmallTools.sleepMills(200);
return "番茄炒蛋";
}).thenCompose(res -> CompletableFuture.supplyAsync(() -> {
SmallTools.printTimeAndThread("服务员打饭");
return res + "+米饭";
})
);
SmallTools.printTimeAndThread("小白在打王者");
SmallTools.printTimeAndThread("小白开吃," + completableFuture.join());
}
4.thenCombine
合并两个没有依赖关系的异步任务的结果,两个异步线程任务同时启动,等待两个任务执行完成后,由BiFunction把两个异步任务执行的结果处理成一个结果在返回
/**
* 厨师炒菜和服务员蒸饭在不同线程中同时开始了,任务都完成后就会汇集在一起
*/
public static void test3() {
SmallTools.printTimeAndThread("小白进入餐厅");
SmallTools.printTimeAndThread("小白点了 番茄炒蛋+一碗米饭");
//thenCombine的作用主要就是 把这两个任务一起执行,得到两个结果,再把这两个结果进行加工成一个结果
CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
SmallTools.printTimeAndThread("厨师炒菜");
SmallTools.sleepMills(2000);
return "番茄炒蛋";
}).thenCombine(CompletableFuture.supplyAsync(() -> {
SmallTools.printTimeAndThread("服务员蒸饭");
SmallTools.sleepMills(2000);
return "米饭";
}), (r1, r2) -> r1 + r2);
SmallTools.printTimeAndThread("小白在打王者");
SmallTools.printTimeAndThread("小白开吃," + completableFuture.join());
}
5.thenApply
1.thenApply 连接两个有依赖关系的任务,都是有同一个线程执行的
2.thenApplyAsync 连接两个有依赖关系的线程任务,由不同线程执行,但是由于使用线程池,会发生线程复用,所以打印出来的线程有的时候是同一个线程
他们与thenCompose的区别是,thenApply是同一个任务线程执行前后两个任务代码的,thenApplyAsync是两个不同线程执行的
//thenApply
public static void test1() {
SmallTools.printTimeAndThread("小白吃完了");
//thenApply 这前后来两个任务都是同一个线程执行的,都是收银员执行
CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
SmallTools.printTimeAndThread("收银员收到小白的付款");
SmallTools.sleepMills(200);
return "200";
}).thenApply((res) -> {
SmallTools.printTimeAndThread("收银员开发票");
SmallTools.sleepMills(200);
return res + "元发票";
});
SmallTools.printTimeAndThread("小白接到一个电话想要一起打游戏");
SmallTools.printTimeAndThread(String.format("小白拿到%s,准备回家", completableFuture.join()));
}
//thenApplySync
public static void test2() {
SmallTools.printTimeAndThread("小白吃完了");
// 两个线程任务执行
CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
SmallTools.printTimeAndThread("收银员收到小白的付款");
SmallTools.sleepMills(200);
return "200";
}).thenApplyAsync((res) -> {
SmallTools.printTimeAndThread("服务员开发票");
SmallTools.sleepMills(200);
return res + "元发票";
});
SmallTools.printTimeAndThread("小白接到一个电话想要一起打游戏");
SmallTools.printTimeAndThread(String.format("小白拿到%s,准备回家", completableFuture.join()));
}
6.applyToEitherAsync
那个任务先结束就把那个任务的结果交给function ,并且没有结束的那个任务会自动被结束掉
// applyToEitherAsync
public static void test3() {
SmallTools.printTimeAndThread("小白走出餐厅,来到公交车站");
//applyToEitherAsync 那个任务先结束就把那个任务的结果交给function ,并且没有结束的那个任务会自动被结束掉
CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
SmallTools.printTimeAndThread("700路公交车正在赶来");
SmallTools.sleepMills(100);
return "700";
}).applyToEitherAsync(CompletableFuture.supplyAsync(() -> {
SmallTools.printTimeAndThread("800路公交车正在赶来");
SmallTools.sleepMills(6000);
SmallTools.printTimeAndThread("800路公交车正在赶来。。。。。。。。。");
return "800";
}), res -> res);
SmallTools.printTimeAndThread(String.format("小白做%s路公交车回家", completableFuture.join()));
}
7.exceptionally
捕获异常
public static void test5() {
SmallTools.printTimeAndThread("小白走出餐厅,来到公交车站");
SmallTools.printTimeAndThread("等待700路或者800路公交车来到公交车站");
//
CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
SmallTools.printTimeAndThread("700路公交车正在赶来");
SmallTools.sleepMills(100);
if(true){
throw new RuntimeException("700路公交撞树了");
}else {
return "700";
}
}).exceptionally(e->{
return e.getMessage();
})
.applyToEitherAsync(CompletableFuture.supplyAsync(() -> {
SmallTools.printTimeAndThread("800路公交车正在赶来");
SmallTools.sleepMills(100);
SmallTools.printTimeAndThread("800路公交车正在赶来。。。。。。。。。");
return "800";
}), res -> {
// if (res.equals("700")) {
// throw new RuntimeException("700路公交撞树了");
// }
return res;
}).exceptionally(e -> {
SmallTools.printTimeAndThread(e.getMessage());
SmallTools.printTimeAndThread("小白叫了出租车");
return "出租车";
});
SmallTools.printTimeAndThread(String.format("小白做%s回家", completableFuture.join()));
}
public static void test4() {
SmallTools.printTimeAndThread("小白走出餐厅,来到公交车站");
SmallTools.printTimeAndThread("等待700路或者800路公交车来到公交车站");
// exceptionally 捕获异常
CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
SmallTools.printTimeAndThread("700路公交车正在赶来");
SmallTools.sleepMills(100);
return "700";
}).applyToEitherAsync(CompletableFuture.supplyAsync(() -> {
SmallTools.printTimeAndThread("800路公交车正在赶来");
SmallTools.sleepMills(100);
SmallTools.printTimeAndThread("800路公交车正在赶来。。。。。。。。。");
return "800";
}), res -> {
if (res.equals("700")) {
throw new RuntimeException("700路公交撞树了");
}
return res;
}).exceptionally(e -> {
SmallTools.printTimeAndThread(e.getMessage());
SmallTools.printTimeAndThread("小白叫了出租车");
return "出租车";
});
SmallTools.printTimeAndThread(String.format("小白做%s回家", completableFuture.join()));
}
8.扩展
-
runAsync(Runnable runnable) 不需要返回值的时候可以是使用该方法
supplyAsync(Supplier supplier) 有返回值 -
thenApply 需要接收前一个返回值,也需要返回结果 BiFunction()
thenRun 不需要接收前一个任务返回值,也不需要返 回结果
thenAccept 接受前一个任务返回值,不需要返回一个结果 consumer() -
thenCombine 需要接收前两个返回值,也需要返回结果 BiFunction()
thenAcceptBoth 接受前两个任务返回值,不需要返回一个结果 consumer()
runAfterBoth 不需要接收前两个任务返回值,也不需要返回结果
4.applyToEither 那个任务先执行完成就会使用那个任务的结果
acceptEither 会得到最快任务执行完的返回值,没有返回结果
runAfterEither 即没有使用返回值,也不需要返回结果
5.exceptionally 捕获异常,并且返回结果
6.handle(BiFunction<Throwable,Object>) 有两个参数,如果前面的任务跑出异常就会收到异常结果,如果是正常的就会收到正常结果。但是无论正常还是异常都会返回一个结果,让后面的程序还是会正常执行
7.whenComplete(BiFunction<Throwable,Object>) 跟handle一样,但是没有返回值
8.不带async的方法就是先当于把后一个任务代码交给 上个执行任务线程执行了
9. 带async的方法,会有两个不同的线程进行执行,但是由于使用线程池,会发生线程复用,所以打印出来的线程有的时候是同一个线程
9.allOf,等待所有的任务执行完成
public static void test2(){
SmallTools.printTimeAndThread("小白和小伙伴们进入餐厅点菜");
long start = System.currentTimeMillis();
CompletableFuture[] completableFutures = (CompletableFuture[]) IntStream.rangeClosed(1, 10)
.mapToObj(i -> new Dish("菜" + i, 1))
.map(dish -> CompletableFuture.runAsync(dish::make))
.toArray(size -> new CompletableFuture[size]);
CompletableFuture.allOf(completableFutures).join();
SmallTools.printTimeAndThread("一共用了" + TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis() - start));
//
SmallTools.printTimeAndThread(String.valueOf(Runtime.getRuntime().availableProcessors()));
}