高级-08异步&线程池(CompletableFuture)
一、线程基础
1、初始化线程的4种方式
- 继承 Thread
- 实现 Runnable 接口
- 实现 Callable 接口 + FutureTask
- 线程池
1.1 继承Thread
class Thread_1 extends Thread{@Overridepublic void run() {System.out.println("当前线程" + Thread.currentThread().getName());}}new Thread_1().start();
1.2 实现 Runnable 接口
class Run implements Runnable{@Overridepublic void run() {System.out.println("当前线程" + Thread.currentThread().getName());}}new Thread(new Run()).start();
1.3 实现 Callable 接口 + FutureTask
可以拿到返回结果,可以处理异常
class Call implements Callable<Integer> {@Overridepublic Integer call() throws Exception {System.out.println("当前线程" + Thread.currentThread().getName());retu 2;}}FutureTask<Integer> futureTask = new FutureTask<>(new Call());new Thread(futureTask).start();Integer integer = futureTask.get();// 阻塞,抛出异常
主进程可以获取线程的运算结果,但是不利于控制服务器中的线程资源。可能导致服务器资源耗尽。
1.4 线程池
业务代码中,之前三种启动线程方式都不会用。
而是【将所有的多线程异步任务交给线程池去执行】
通过线程池控制资源,性能稳定,也可以获取执行结果,并捕获异常。
但是,在业务复杂情况下,一个异步调用可能会依赖于另一个异步调用的结果。
// 最好保证当前系统中只有一两个线程池public static ExecutorService service = Executors.newFixedThreadPool(10);// submit可以获取返回值Future<Integer> submit = service.submit(new Call());Integer in = submit.get();// execute没有返回值service.execute(new Run());
还可以自定义线程池
2、开发中为什么使用线程池
- 降低资源的消耗
- 通过重复利用已经创建好的线程降低线程的创建和销毁带来的損耗。
- 提高响应速度
- 因为线程池中的线程数没有超过线程池的最大上限时,有的线程处于等待分配任务的状态,当任务来时无需创建新的线程就能执行。
- 提高线程的可管理性
- 线程池会根据当前系统特点对池内的线程进行优化处理,减少创建和销毁线程带来的系统开销。无限的创建和销毁线程不仅消耗系统资源,还降低系统的稳定性,使用线程池进行统一分配。
二、CompletableFuture 异步编排
1、创建异步对象
public static ExecutorService executor = Executors.newFixedThreadPool(10);// 1、runAsync没有返回值CompletableFuture.runAsync(() -> { System.out.println("Runnable 的 run 方法");}, executor);// 2、supplyAsync可以获取返回值CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> { retu "返回值";}, executor);completableFuture.get(); // 接收返回值,阻塞
2、计算完成时回调方法
CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> { retu "返回值"; // 可以返回值}, executor).whenComplete((res, exception) -> {// 感知异常,但无法修改数据System.out.println("异步任务成功完成了");System.out.println("异步任务结果:" + res + ";异常为:" + exception);}).exceptionally(throwable -> {// 感知异常,并且返回默认值retu "默认返回值";});completableFuture.get();
方法不以 Async 结尾,意味着 Action 使用相同的线程执行,而 Async 可能会使用其他线程执行(如果是使用相同的线程池,也可能会被同一个线程选中执行)
3、handle
任务执行完的处理方法
CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {retu "返回值"; // 可以返回值}, executor).handle((res,exception)->{if(res == null){retu "";}retu res + "01";});completableFuture.get();
4、线程串行化方法
5、两任务组合-都要完成
// 定义两个异步对象CompletableFuture<String> future01 = CompletableFuture.supplyAsync(() -> {retu "返回值1";}, executor);CompletableFuture<String> future02 = CompletableFuture.supplyAsync(() -> {retu "返回值2";}, executor);// 两任务组合future01.runAfterBothAsync(future02,()->{System.out.println("run 线程03");},executor);future01.thenAcceptBothAsync(future02,(res1,res2)->{System.out.println("之前结果" + res1 + "》》" + res2);},executor);future01.thenCombineAsync(future02,(res1,res2)->{retu res1 + res2;},executor);
6、两任务组合-一个完成
7、多任务组合
allOf 等待所有任务完成
anyOf 只要有一个任务完成
CompletableFuture<Void> all = CompletableFuture.allOf(future01, future02, future03);all.get(); // 阻塞,不加这个的话主线程会继续走// 分别获取各个线程的返回值String s = future01.get();CompletableFuture<Object> any = CompletableFuture.anyOf(future01, future02, future03);Object o = any.get(); // 获取最先完成线程的返回值
作者:这杯Java有毒
来源链接:https://www.cnblogs.com/yanyaqiblog/p/13965873.html
版权声明:
1、JavaClub(https://www.javaclub.cn)以学习交流为目的,由作者投稿、网友推荐和小编整理收藏优秀的IT技术及相关内容,包括但不限于文字、图片、音频、视频、软件、程序等,其均来自互联网,本站不享有版权,版权归原作者所有。
2、本站提供的内容仅用于个人学习、研究或欣赏,以及其他非商业性或非盈利性用途,但同时应遵守著作权法及其他相关法律的规定,不得侵犯相关权利人及本网站的合法权利。
3、本网站内容原作者如不愿意在本网站刊登内容,请及时通知本站(javaclubcn@163.com),我们将第一时间核实后及时予以删除。