优化代码时,除了@Async注解,项目中如何使用多线程异步调用?
举个例子,去餐厅吃饭的时候。先点餐,厨师做菜,在厨师做菜的时候打游戏,然后根据厨师做的菜的口味去买矿泉水还是可乐。这样,厨师做菜并没有阻塞你打游戏,并且还是能接收到厨师任务的结果。与2个任务同步进行相比缩短了时间。
CompletableFuture
FutureTask也行,但不用
- 工具类
public class SmallTools {
public static void sleepMillis(long millis){
try {
Thread.sleep(millis);
}catch (InterruptedException exception){
exception.printStackTrace();
}
}
public static void printTimeAndThread(String tag){
String result = new StringJoiner("\t|\t")
.add(String.valueOf(System.currentTimeMillis()))
.add(String.valueOf(Thread.currentThread().getId()))
.add(Thread.currentThread().getName())
.add(tag)
.toString();
System.out.println(result);
}
}
- 测试1 说明CompletableFuture.supplyAsync会立即在后台线程池中异步执行 lambda 表达式,不需要显式调用。
public class Main {
public static void main(String[] args) {
SmallTools.printTimeAndThread("小白进入餐厅");
SmallTools.printTimeAndThread("小白点了 番茄炒蛋 + 一碗米饭");
CompletableFuture<String> cf1 = CompletableFuture.supplyAsync(() -> {
SmallTools.printTimeAndThread("厨师炒菜");
SmallTools.sleepMillis(4000);
SmallTools.printTimeAndThread("cf1线程结束");
return "番茄炒蛋";
});
SmallTools.printTimeAndThread("小白开始打王者");
SmallTools.sleepMillis(5000);
SmallTools.printTimeAndThread("主线程结束");
}
}
------
1715099888262 | 1 | main | 小白进入餐厅
1715099888262 | 1 | main | 小白点了 番茄炒蛋 + 一碗米饭
1715099888279 | 1 | main | 小白开始打王者
1715099888279 | 9 | ForkJoinPool.commonPool-worker-9 | 厨师炒菜
1715099892285 | 9 | ForkJoinPool.commonPool-worker-9 | cf1线程结束
1715099893282 | 1 | main | 主线程结束
- 测试2 同时开启多个异步调用
public class Main {
public static void main(String[] args) {
SmallTools.printTimeAndThread("小白进入餐厅");
SmallTools.printTimeAndThread("小白点了 番茄炒蛋 + 一碗米饭");
CompletableFuture<String> cf1 = CompletableFuture.supplyAsync(() -> {
SmallTools.printTimeAndThread("厨师炒菜");
SmallTools.sleepMillis(4000);
SmallTools.printTimeAndThread("cf1线程结束");
return "番茄炒蛋";
});
CompletableFuture<String> cf2 = CompletableFuture.supplyAsync(() -> {
SmallTools.printTimeAndThread("服务员打饭");
SmallTools.sleepMillis(4000);
SmallTools.printTimeAndThread("cf2线程结束");
return "饭";
});
SmallTools.printTimeAndThread("小白开始打王者");
SmallTools.sleepMillis(5000);
SmallTools.printTimeAndThread("主线程结束");
}
}
------
1715100175308 | 1 | main | 小白进入餐厅
1715100175308 | 1 | main | 小白点了 番茄炒蛋 + 一碗米饭
1715100175325 | 1 | main | 小白开始打王者
1715100175326 | 9 | ForkJoinPool.commonPool-worker-9 | 厨师炒菜
1715100175326 | 10 | ForkJoinPool.commonPool-worker-2 | 服务员打饭 //睡了4秒
1715100179338 | 9 | ForkJoinPool.commonPool-worker-9 | cf1线程结束
1715100179338 | 10 | ForkJoinPool.commonPool-worker-2 | cf2线程结束
1715100180331 | 1 | main | 主线程结束
Process finished with exit code 0
- 测试3 等待异步调用的结果,根据结果再处理逻辑
public class Main {
public static void main(String[] args) {
SmallTools.printTimeAndThread("小白进入餐厅");
SmallTools.printTimeAndThread("小白点了 番茄炒蛋 + 一碗米饭");
CompletableFuture<String> cf1 = CompletableFuture.supplyAsync(() -> {
SmallTools.printTimeAndThread("厨师炒菜");
SmallTools.sleepMillis(4000);
SmallTools.printTimeAndThread("厨师打饭");
SmallTools.sleepMillis(2000);
return "番茄炒蛋 + 米饭";
});
SmallTools.printTimeAndThread("小白开始打王者");
SmallTools.printTimeAndThread(String.format("%s做好了,小白开吃",cf1.join()));
}
}
----结果
1715098991592 | 1 | main | 小白进入餐厅
1715098991592 | 1 | main | 小白点了 番茄炒蛋 + 一碗米饭
1715098991608 | 1 | main | 小白开始打王者
1715098991610 | 9 | ForkJoinPool.commonPool-worker-9 | 厨师炒菜 //睡了4秒
1715098995625 | 9 | ForkJoinPool.commonPool-worker-9 | 厨师打饭 //睡了2s
1715098997637 | 1 | main | 番茄炒蛋 + 米饭做好了,小白开吃
- 测试4 链式调用,开启2个线程,第二个线程等待第一个线程结束后在异步调用
public class Main {
public static void main(String[] args) {
SmallTools.printTimeAndThread("小白进入餐厅");
SmallTools.printTimeAndThread("小白点了 番茄炒蛋 + 一碗米饭");
CompletableFuture<String> cf1 = CompletableFuture.supplyAsync(() -> {
SmallTools.printTimeAndThread("厨师炒菜");
SmallTools.sleepMillis(4000);
return "番茄炒蛋";
}).thenCompose(dish -> CompletableFuture.supplyAsync(()->{
SmallTools.printTimeAndThread("厨师打饭");
SmallTools.sleepMillis(2000);
return " + 米饭";
}));
SmallTools.printTimeAndThread("小白开始打王者");
SmallTools.printTimeAndThread(String.format("%s 做好了,小白开吃",cf1.join()));
}
}
------
1715100509306 | 1 | main | 小白进入餐厅
1715100509306 | 1 | main | 小白点了 番茄炒蛋 + 一碗米饭
1715100509323 | 9 | ForkJoinPool.commonPool-worker-9 | 厨师炒菜
1715100509323 | 1 | main | 小白开始打王者 //等待4秒
1715100513328 | 9 | ForkJoinPool.commonPool-worker-9 | 厨师打饭 //等待2秒
1715100515342 | 1 | main | + 米饭 做好了,小白开吃
- 测试5 同时开启2个异步调用,其实和测试2没什么区别,代码好看点
public class Main {
public static void main(String[] args) {
SmallTools.printTimeAndThread("小白进入餐厅");
SmallTools.printTimeAndThread("小白点了 番茄炒蛋 + 一碗米饭");
CompletableFuture<String> cf1 = CompletableFuture.supplyAsync(() -> {
SmallTools.printTimeAndThread("厨师炒菜");
SmallTools.sleepMillis(4000);
return "番茄炒蛋";
}).thenCombine(CompletableFuture.supplyAsync(() -> {
SmallTools.printTimeAndThread("服务员蒸饭");
SmallTools.sleepMillis(4000);
return "米饭";
}),(dish,rice) -> {
SmallTools.printTimeAndThread("服务员打饭");
return dish + " + " +rice;
});
SmallTools.printTimeAndThread("小白开始打王者");
SmallTools.printTimeAndThread(String.format("%s 做好了,小白开吃",cf1.join()));
}
}
------
1715101146820 | 1 | main | 小白进入餐厅
1715101146820 | 1 | main | 小白点了 番茄炒蛋 + 一碗米饭
1715101146837 | 9 | ForkJoinPool.commonPool-worker-9 | 厨师炒菜
1715101146837 | 10 | ForkJoinPool.commonPool-worker-2 | 服务员蒸饭
1715101146838 | 1 | main | 小白开始打王者 //睡了4秒
1715101150852 | 9 | ForkJoinPool.commonPool-worker-9 | 服务员打饭
1715101150858 | 1 | main | 番茄炒蛋 + 米饭 做好了,小白开吃