Java8 CompletableFuture 创建线程 supplyAsync runAsync
一、前言
Java8推出了CompletableFuture,它实现了很多功能,用它编写的代码也比较优雅。
目前大多数公司用的JDK已经从6升级到8及以上了,那么作为一名合格的Java打工人,除了要掌握之前的FutureTask和CompletionService,也要掌握CompletableFuture。
否则你在项目中不用Lambda,也不用CompletableFuture,那你JDK升级到8,升个寂寞?
二、CompletableFuture创建新线程
CompletableFuture,默认依靠fork/join框架启动新的线程实现异步与并发的。
它提供了函数式编程的能力,可以通过回调函数的方式处理返回结果,并且提供了转换和组合CompletableFuture的方法。
在用CompletableFuture之前,我认为得先熟悉下Java8的Lambda编程语法,《Java8实战》这本书非常适合阅读。
CompletableFuture创建线程有2种方式:supplyAsync(有返回值)和:runAsync(无返回值)。
1. 创建测试用的业务类
创建一个DeptService,模拟根据Id获取部门的方法getById(Integer id)。
public class DeptService {
public Dept getById(Integer id) {
System.out.println("线程:" + Thread.currentThread().getName() + " getById(" + id + ")");
if (id == 1){
return new Dept(1, "研发一部");
} else if (id == 2){
return new Dept(2, "研发二部");
} else {
throw null;
}
}
}
创建一个UserService ,模拟getById()和save()这2个方法。
public class UserService {
//根据Id获取User
public User getById(Integer id) throws Exception {
System.out.println("线程:" + Thread.currentThread().getName() + " getById(" + id + ")");
if (id == 1){
return new User(1, "冬哥", 31);
} else if (id == 2){
return new User(2, "珣爷", 30);
} else {
throw new Exception("未能找到人员");
}
}
//保存User
public User save(User user){
System.out.println("线程:" + Thread.currentThread().getName() + " save()," + user.toString());
return user;
}
}
2. supplyAsync(有返回值)
supplyAsync有2种,第二个需要多传1个线程池的实现。
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier)
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier,
Executor executor)
需求:
- 在main线程里创建一个线程异步获取id=1的部门
- 在main线程里获取新线程的返回值
supplyAsync()中调取deptService.getById(1),并return它的值。
public class Thread01_SupplyAsync {
public static void main(String[] args) throws ExecutionException, InterruptedException {
DeptService deptService = new DeptService();
CompletableFuture<Dept> deptCompletableFuture = CompletableFuture.supplyAsync(() -> {
Dept dept = deptService.getById(1);
return dept;
});
System.out.println("线程:" + Thread.currentThread().getName() +
" 结果:" + deptCompletableFuture.get());
}
}
运行结果如下,可见DeptService是在新的线程里执行的,而在main主线程中调用get()获取线程的结果会阻塞主线程。
线程:ForkJoinPool.commonPool-worker-1 getById(1)
线程:main 结果:Dept{id=1, name='研发一部'}
3. runAsync(无返回值)
runAsync适用无返回值的情况,也有2种,第二个需要多传1个线程池的实现
public static CompletableFuture<Void> runAsync(Runnable runnable)
public static CompletableFuture<Void> runAsync(Runnable runnable,
Executor executor)
对上面SupplyAsync的测试代码稍做修改,runAsync()里没有返回值,所以去掉return。
public class Thread02_RunAsync {
public static void main(String[] args) throws ExecutionException, InterruptedException {
DeptService deptService = new DeptService();
CompletableFuture<Void> voidCompletableFuture = CompletableFuture.runAsync(() -> {
deptService.getById(1);
});
System.out.println("线程:" + Thread.currentThread().getName() +
" 结果:" + voidCompletableFuture.get());
}
}
运行结果如下,可以看出runAsync也是创建了新线程,调用get方法只能返回null。
线程:ForkJoinPool.commonPool-worker-1 getById(1)
线程:main 结果:null