创建线程的几种方法:
执行完整个main方法,在后台慢慢打印。
1.2.3方式都不能获得控制资源。
4、 线程池方法,每个异步任务,提交给线程池让他自己去执行就行。
1、降低资源的消耗
通过重复利用已经创建好的线程降低线程的创建和销毁带来的损耗
2、提高响应速度
因为线程池中的线程数没有超过线程池的最大上限时,有的线程处于等待分配任务的状态,当任务来时无需创建新的线程就能执行
3、提高线程的可管理性
线程池会根据当前系统特点对池内的线程进行优化处理,减少创建和销毁线程带来的系统开销。无限的创建和销毁线程不仅消耗系统资源,还降低系统的稳定性,使用线程池进行统一分配
java">public static ExecutorService service = Executors.newFixedThreadPool(10);
service.submit(new Runable01()) //有返回值
service.excute() //void
核心线程不释放。
小小面试题
异步使用的基本方法:
使用当前线程池
supplyAsync有返回值
runAsync没有返回值。
Async会创建一个新的线程,没有Async是从当前线程调用。
出现异常
1、when可以感知,但是不可以更改
2.exception可以修改结果
3、handle方法执行后的处理。
线程床串行化。
1、then
2、accept可以感知到上一步的执行结果(返回值)
3、apply可以感知到上一步和下一步的执行结果 (返回值)
4、run仅仅处理代码。
5、Async和上面都相同
6、both表示两个任务都完成后才执行下面的语句。
7、combin合并多个。
f1,f2表示上一个的返回值
8、 Either 表示两个执行完其中一个就执行下一个
这段代码的执行流程
-
future01
和future02
异步执行:CompletableFuture.supplyAsync
启动任务时,会提交任务给线程池(executor
),任务在后台线程执行,而主线程不会阻塞,继续向下执行代码。- 任务1和任务2的执行顺序取决于线程调度,无法保证谁先执行完。
-
主线程继续执行:
- 主线程会跳过
future01
和future02
的异步任务代码块,直接执行System.out.println("main.....end....")
这行代码。
- 主线程会跳过
-
runAfterBothAsync
的触发:runAfterBothAsync
会等待future01
和future02
都完成后才触发。- 但是
runAfterBothAsync
是异步执行,它并不会阻塞主线程。
执行顺序解释
为什么 main.....end....
出现在 "任务2结束" 和 "任务3开始..." 之间?
-
主线程继续执行:
- 主线程先执行了
System.out.println("main.....end....")
。 - 此时,
future01
和future02
仍然在执行或者已经执行完,线程池中的线程在后台完成任务。
- 主线程先执行了
-
任务2结束:
- 任务2(
future02
)的线程在异步执行结束后,输出"任务2结束:"
。
- 任务2(
-
runAfterBothAsync
执行:- 当
future01
和future02
都完成时,runAfterBothAsync
的回调被触发,输出"任务3开始..."
。
- 当
由于 main.....end....
的输出是在主线程中执行的,而任务1、任务2 和 runAfterBothAsync
都是在后台线程异步执行,因此主线程和后台线程的输出会交错,具体顺序由线程调度决定。
阻塞式获取:假设代码流程
java">CompletableFuture<Integer> future01 = CompletableFuture.supplyAsync(() -> {System.out.println("任务1线程: " + Thread.currentThread().getId());System.out.println("任务1结束");return 1;
});CompletableFuture<String> future02 = CompletableFuture.supplyAsync(() -> {System.out.println("任务2线程: " + Thread.currentThread().getId());System.out.println("任务2结束");return "Hello";
});CompletableFuture<Double> future03 = CompletableFuture.supplyAsync(() -> {System.out.println("任务3线程: " + Thread.currentThread().getId());System.out.println("任务3结束");return 3.14;
});// 主线程调用 get() 方法
Integer result1 = future01.get();
String result2 = future02.get();
Double result3 = future03.get();System.out.println("main....end....结果1: " + result1 + ", 结果2: " + result2 + ", 结果3: " + result3);
执行流程分析
-
三个异步任务:
future01
、future02
和future03
会被提交到线程池的后台线程执行。- 它们的输出顺序是不确定的,因为它们在不同的线程中异步执行。
-
调用
get()
方法:- 主线程会按照顺序依次调用
future01.get()
、future02.get()
和future03.get()
。 - 主线程在每个
get()
调用处会被阻塞,直到对应的CompletableFuture
任务完成并返回结果。 - 由于主线程是顺序阻塞的,任务执行完的结果会按照主线程的调用顺序依次获取
- 主线程会按照顺序依次调用
结果展示
任务3结束
任务1结束
任务2结束
main...end...结果: 1, Hello, 3.14
总耗时: 3010ms
注意虽然是阻塞式调用,但是 只会等待没有完成的部分。所以最后还是输出3s