CompletableFuture
java8新特性,异步线程。可以简化多线程的创建过程,小编使用了之后腰也不酸了,手敲代码也不疼了。
因为减少了创建线程的过程还有相关线程执行逻辑,减少20多行代码吧,可能更多
使用
CompletableFuture.runAsync不返回值
CompletableFuture.supplyAsync返回值
上面两个是主要的执行方法。
对返回值的处理
thenAccept对返回值的消费,如果出现异常也不会返回值
所以如果你保证自己的代码不会出现异常,或者异常在业务逻辑已经捕获就可以使用它来消费,不然异常都被吃掉,这时使用get()方法获取到的是null
异常处理
public static void main(String[] args) {
String[] a={"1","3","2"};
Arrays.asList(a).forEach(u->{
CompletableFuture<Integer> future =CompletableFuture.supplyAsync(() -> doSomeThing(u)).exceptionally(e-> {
System.out.println(e.toString());
return 2;
});
try {
System.out.println(future.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
});
}
public static Integer doSomeThing(String url) {
int i = 1/0 ;
return i;
}
捕获代码
exceptionally()里面是相关的处理
由于采用supplyAsync执行方法所以捕获也有返回值,如果出现错误的话返回相应的错误值。
阻塞获取值
get()方法
会一直阻塞到拿到值为止。
如果不进行exceptionally捕获异常,就会在get里面直接抛处理!
配置线程池
默认是ForkJoinPool,单独配置需要这样写
指定线程池
执行任务
举个栗子
private ExecutorService executor = Executors.newCachedThreadPool();
CompletableFuture.runAsync(() -> doSomeThing(url), executor)
注意点
线程池数量问题
- 如果是CPU密集型任务(大量复杂计算逻辑),线程池数跟cpu数需要去计算的,baidu一下
- 如果是IO密集型任务(只是什么增删改查等等,简单任务),这个线程数可以大点。类似上面这样的栗子,由于简单任务,直接打满cpu都可以,就是要快,压榨cpu
与并行流的区别
并行流是无序的,CompletableFuture虽然也是无需的,但是可以阻塞获取到相应的值后再执行下去。
执行某下载然后打包逻辑
urlList.stream().parallel().forEach(url -> {
try {
CompletableFuture.runAsync(() -> doSomeThing(url), executor).get();
} catch (InterruptedException e) {
log.error("download出现InterruptedException异常信息:{}", e.toString());
Thread.currentThread().interrupt();
} catch (ExecutionException e) {
log.error("download出现ExecutionException异常信息:{}", e.toString());
}
});
我们需要一个个下载到获取值才能算全部下载完成,所以使用get阻塞到获取值。
这里使用并行流和CompletableFuture异步,西西。飞快的感觉…