最直接等待线程结束的方式是调用其join()方法;countdownlatch适用于多线程协同启动或收尾;executorservice+future通过get()控制结果获取顺序;synchronized和volatile不保证执行顺序。

用 join() 强制等待指定线程结束
最直接、最常用的方式是让当前线程调用另一个线程的 join() 方法。它会阻塞当前线程,直到目标线程执行完毕。适用于已知明确依赖关系的场景,比如“线程B必须等线程A跑完再启动”。
注意点:
-
join()是实例方法,必须在目标线程start()之后调用才有效;调用过早(如线程还没 start)不会报错,但可能立即返回,起不到等待效果 - 可带超时参数:
thread.join(5000),避免无限等待;超时后当前线程继续执行,不抛异常 - 若被等待的线程抛出未捕获异常并终止,
join()仍会正常返回——它只等“结束”,不管是否异常退出
用 CountDownLatch 实现多线程协同启动或顺序收尾
当需要多个线程按固定阶段推进(比如“全部初始化完成后再统一开始计算”),CountDownLatch 比反复 join() 更灵活。它本质是一个倒计时门闩,构造时指定计数,每次 countDown() 减一,await() 阻塞直到归零。
典型用法示例:
立即学习“Java免费学习笔记(深入)”;
CountDownLatch latch = new CountDownLatch(2);
new Thread(() -> { /* task A */ latch.countDown(); }).start();
new Thread(() -> { /* task B */ latch.countDown(); }).start();
latch.await(); // 主线程等 A 和 B 都完成
System.out.println("A and B done, now start C");
new Thread(() -> { /* task C */ }).start();
关键提醒:
- 计数值不能重置,用完即废;需重复使用请换
CyclicBarrier -
await()可响应中断,抛InterruptedException,务必处理或重新设置中断状态 - 不要在
countDown()前就调用await()—— 虽然不会死锁,但可能导致逻辑提前触发
用 ExecutorService + Future 控制提交与获取顺序
在使用线程池时,不能靠 submit() 的调用顺序保证执行顺序,因为任务可能被不同工作线程并发执行。但你可以靠 Future.get() 的调用顺序来强制“获取结果”的顺序,间接控制后续逻辑流。
例如:
ExecutorService exec = Executors.newFixedThreadPool(3); Future<String> f1 = exec.submit(() -> doStep1()); Future<String> f2 = exec.submit(() -> doStep2()); // 不依赖 f1 结果 Future<String> f3 = exec.submit(() -> doStep3()); <p>// 按需阻塞:先等 f1 完成,再等 f3,f2 可并行 String r1 = f1.get(); // 阻塞 String r3 = f3.get(); // 阻塞 String r2 = f2.get(); // 此时很可能已就绪,get() 很快返回
要注意:
-
Future.get()是阻塞操作,且会传播任务内抛出的异常(包装为ExecutionException) - 如果任务本身有依赖(比如 step3 必须用 step1 的输出),那就得显式传递参数,
Future本身不提供依赖调度能力 - 线程池大小会影响实际并发度;即使你按顺序
get(),若池子太小,step2 可能被卡住排队,拖慢整体
别踩这些坑:volatile / synchronized 不能保证执行顺序
有人误以为加 synchronized 块或用 volatile 变量就能控制线程执行先后——其实它们只解决可见性和原子性,不构成执行顺序约束。
比如下面这段代码:
synchronized(lock) { System.out.println("A"); }
synchronized(lock) { System.out.println("B"); }
这只能保证 A 和 B 不会交叉打印,但无法确保“线程1一定先执行A、线程2一定后执行B”。谁抢到锁谁先跑,仍是竞争决定的。
真正要顺序执行,核心思路只有一个:让后一个动作**显式等待前一个动作的完成信号**。这个信号可以是 join()、CountDownLatch、Future.get()、BlockingQueue.take(),甚至只是一个 while(!done) Thread.sleep(1)(不推荐,但原理一致)。没有“自动顺序”,只有“主动等待”。










