深入并发编程之异步回调
前言
通过上一节的学习,我们明白了什么是任务。但是我为什么没有去分析线程池的submit方法呢?因为在实际工作中我们并不用到它。
线程的异步
public static void main(String[] args){
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
1, // 核心线程数
2, // 最大线程数
60, // 线程存活时间
TimeUnit.SECONDS, // 存活时间单位
new LinkedBlockingQueue<>(),// 阻塞队列
Executors.defaultThreadFactory(),// 线程工厂
new ThreadPoolExecutor.AbortPolicy());// 拒绝策略
Future<String> submit = threadPoolExecutor.submit(() -> {
return "hello future";
});
}
1.1 Future是什么?
通过上述代码,我们可以看到,通过线程池的submit方法会返回一个Future对象,我们点进去发现:
Future是一个接口,它里面定义了一些通用方法,例如任务是否完成,获取任务结果等。
再看看submit里面到底是什么:
可以看到,底层是 new 了一个 FutureTask对象。
1.2. FutureTask
那么我们怎么能获取到异步执行结果呢?
第一种是调用 get 方法。这会阻塞主线程,很明显这跟单线程没啥区别。
第二种是调用 isDone方法循环获取结果。这样虽然不影响主线程执行,但是如果子任务耗时,会导致CPU空转,影响性能。而且这样某种意义上来说还是单线程,万一主线程需要等到子任务结果才能继续执行呢?
1.3. CompletableFuture
这是Java8的一个新类,同样实现了Future接口,除此之外还实现了一个新接口,可以理解为步骤。(即如果下一个子任务需要上一个子任务的执行结果,可以直接串联执行。)
public static void main(String[] args) throws ExecutionException, InterruptedException {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
1, // 核心线程数
2, // 最大线程数
60, // 线程存活时间
TimeUnit.SECONDS, // 存活时间单位
new LinkedBlockingQueue<>(),// 阻塞队列
Executors.defaultThreadFactory(),// 线程工厂
new ThreadPoolExecutor.AbortPolicy());// 拒绝策略
CompletableFuture.supplyAsync(() -> {
try {
System.out.println("进入第一个子任务");
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return "hello CompletableFuture";
}, threadPoolExecutor)
.thenApply(s -> {
System.out.println("进入第二个子任务");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return s;
}).thenAccept(
System.out::println
);
System.out.println("主线程执行11");
System.out.println("主线程执行22");
System.out.println("主线程执行33");
}