前言
- jdk1.8前,使用future获取结果的情况比较多,但是这种使用方式有个缺点,就是调用future.get()的时候会阻塞主线程,如果第一个任务的future.get()一直阻塞,那么后面的结果也就一直被阻塞了。因此从1.8开始可以使用CompletableFuture去实现,多线程异步执行任务,并且在主线程最终等待所有的任务返回结果。达到多线程处理的目的。
一、代码示例
public static void main(String[] args) {
// 1.新建线程池
ExecutorService executorService = Executors.newFixedThreadPool(10);
// 2.存放异步返回结果
List<Integer> result = new ArrayList<>();
// 3.存放需要互相等待的异步线程的resultfuture
CompletableFuture<Integer> [] resultFuture = new CompletableFuture[2];
//4. 这里模拟了两个线程任务,计算1+1=2 并且返回结果
CompletableFuture<Integer> resultFuture1 = CompletableFuture.supplyAsync(() -> {
return 1 + 1;
},executorService).whenComplete((r,t)->{
result.add(r);
});
resultFuture[0]=resultFuture1;
//5.执行第二个线程任务2+2=4,并且等待执行结果
CompletableFuture<Integer> resultFuture2 = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return 2 + 2;
}).whenComplete((r, t) -> {
result.add(r);
});
resultFuture[1]=resultFuture2;
//6.需要相互等待的线程
CompletableFuture<Void> voidCompletableFuture = CompletableFuture.allOf(resultFuture);
long begin = System.currentTimeMillis();
// 7.开始阻塞获取结果
voidCompletableFuture.join();
// 8记录耗时
System.out.println("耗时:"+(System.currentTimeMillis()-begin));
// 9.输出结果
for (Integer temp: result) {
System.out.println("结果:"+temp);
}
// 10.关掉线程池,不关掉,main方法会一直运行。
executorService.shutdown();
}
运行结果截图如下:
总结
1.这里需要说明的是异步线程CompletableFuture.allOf方法仅支持可变参数类型,也就是这里是不能放list结果的,这里需要使用数组去存放需要等待结果的线程future。
2.CompletableFuture 如果是有返回值使用supplyAsync,如果是没有返回值的执行runAsync使用上都差不多就不做示例了。
3.CompletableFuture 执行上面的两个方法的时候可以自己传入线程池,也可以不传入线程池,如果不传入线程池,则使用的是forkjoin生成的comonpool去执行,这种线程池需要多核cup才能发挥优势,不过现在基本没有服务器是单核了吧,这个了解下就行。
4.还有线程池最好是自己通过new ThreadPoolExecutor创建,而不是和我一样,我只是为了快速写代码偷懒,例如使用本实例的方式创建线程池,缺点就是线程池的队列最大值是int的最大值,意味着可以放非常多的线程,这就有可能导致内存溢出如果线程非常多的时候。这是不严谨的。