背景
- 常见的函数式接口,就是对函数编程的应用
- Runnable 没有返回值的函数式接口
- Callable 有返回值的函数式接口
使用线程池
- 一般来说,很少使用
new Thread(函数对象)
这种方式来直接 创建线程,更多的时候使用的线程成来集中管理线程,避免频繁开关线程造成的资源浪费
- 线程池对象的调用使用
submit
方法,传入一个函数对象
,可以使callable的,也可以是runnable的- 如果有返回值,返回值使用
Future
来接受,同时返回类型和Future的泛型保持一致
- 调用线程方法get之后,会开辟新的线程继续执行,但是主线程会一直
阻塞等待线程的返回结果
java">ExecutorService executorService = Executors.newFixedThreadPool(3);
Future<String> thread = executorService.submit(() -> {System.out.println("线程1开始执行");Thread.sleep(5000);System.out.println("线程1执行完毕");return "success1";
});
Future<String> thread2 = executorService.submit(() -> {System.out.println("线程2开始执行");Thread.sleep(10000);System.out.println("线程2执行完毕");return "success2";
});
try {// 这种形式会将线程进行阻塞,等到两个线程执行完毕后才会继续执行System.out.println("主线程开始执行");String s = thread.get();System.out.println("线程执行中");String s1 = thread2.get();System.out.println("线程执行完毕"+s+s1);System.out.println("主线程执行完毕");
} catch (InterruptedException e) {throw new RuntimeException(e);
} catch (ExecutionException e) {throw new RuntimeException(e);
}
上述方案的缺点
显式的使用线程池
,一般程序员对于线程池的使用并不熟练- 使用匿名函数的方式书写方法体,如果后续的方法体逻辑复杂的话,可能有很多嵌套,
代码的可读性将会变得很差
- 解决方案:使用1.8引入的
CompletableFuture
- 这个解决方案的优点,你可以将 数据的处理过程进行
拆分
,同时将上一步的结果返回给下一步进行处理,这种方式类似于前端的then ,这种解决方式类似于前端解决回调地狱的操作
同步消费 结果
thenAccept
将得到的结果同步消费
java"> CompletableFuture<String> stringCompletableFuture1 = CompletableFuture.supplyAsync(() -> {System.out.println("异步任务1开始");try {Thread.sleep(10000);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("异步任务1结束");return "异步任务1";});CompletableFuture<String> stringCompletableFuture2 = CompletableFuture.supplyAsync(() -> {System.out.println("异步任务2开始");try {Thread.sleep(5000);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("异步任务2结束");return "异步任务2";});// 将得到的两个线程的任务 直接同步执行stringCompletableFuture1.thenAccept(System.out::println);stringCompletableFuture2.thenAccept(System.out::println);System.in.read();// 阻塞主线程 主线程不要那么早结束
异步消费 结果
得到结果之后,将结果丢给一个consumer,
开启一个新的线程来消费这个结果
java"> CompletableFuture<String> stringCompletableFuture1 = CompletableFuture.supplyAsync(() -> {System.out.println("异步任务1开始");try {Thread.sleep(10000);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("异步任务1结束");return "异步任务1";});CompletableFuture<String> stringCompletableFuture2 = CompletableFuture.supplyAsync(() -> {System.out.println("异步任务2开始");try {Thread.sleep(5000);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("异步任务2结束");return "异步任务2";});// 将得到的两个线程的任务 异步执行stringCompletableFuture1.thenAcceptAsync(r -> {try {Thread.sleep(10000);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println(r);System.out.println("异步任务1的结果消费完毕");});stringCompletableFuture2.thenAcceptAsync(r -> {try {Thread.sleep(5000);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println(r);System.out.println("异步任务2的结果消费完毕");});System.in.read();// 阻塞主线程 主线程不要那么早结束
异步转换数据
一般来说thenApplyAsync 转换之后,得到的还是一个CompletableFuture,一般再转换之后还是需要一个Consumer进行消费
java"> // 异步转换 再将转换后的结果同步消费stringCompletableFuture1.thenApplyAsync(r -> {try {Thread.sleep(10000);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println(r);System.out.println("异步任务1的结果消费完毕");return r + "转换";}).thenAccept(System.out::println);