在Java中,保证多个线程按特定顺序执行的核心是通过线程同步机制控制执行时序。以下是几种实现线程顺序执行的常见方法,附代码示例和适用场景分析:
1. 使用 Thread.join()
:最简单直观
通过主线程依次启动并等待前一线程完成:
public class JoinDemo {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> System.out.println("T1"));
Thread t2 = new Thread(() -> System.out.println("T2"));
Thread t3 = new Thread(() -> System.out.println("T3"));
t1.start();
t1.join(); // 主线程等待T1结束
t2.start();
t2.join(); // 主线程等待T2结束
t3.start();
}
}
优点:实现简单,无需复杂同步。
缺点:主线程阻塞,无法并行执行其他任务。
2. 单线程线程池:任务队列保证顺序
利用单线程池的FIFO特性顺序执行任务:
ExecutorService executor = Executors.newSingleThreadExecutor();
executor.submit(() -> System.out.println("T1"));
executor.submit(() -> System.out.println("T2"));
executor.submit(() -> System.out.println("T3"));
executor.shutdown();
优点:代码简洁,天然有序,自动管理线程生命周期。
缺点:非真正多线程,任务串行执行。
3. CompletableFuture
链式调用:异步编排
通过链式调用实现任务依赖:
CompletableFuture
.runAsync(() -> System.out.println("T1"))
.thenRun(() -> System.out.println("T2"))
.thenRun(() -> System.out.println("T3"))
.join(); // 等待所有任务完成
优点:异步非阻塞,可扩展复杂依赖。
缺点:依赖线程池配置,需理解异步编程模型。
4. 使用 CountDownLatch
:精准控制
通过计数器协调线程执行顺序:
CountDownLatch latch1 = new CountDownLatch(1);
CountDownLatch latch2 = new CountDownLatch(1);
new Thread(() -> {
System.out.println("T1");
latch1.countDown();
}).start();
new Thread(() -> {
try {
latch1.await(); // 等待T1完成
System.out.println("T2");
latch2.countDown();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();
new Thread(() -> {
try {
latch2.await(); // 等待T2完成
System.out.println("T3");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();
优点:灵活控制复杂依赖,支持多线程协作。
缺点:代码较复杂,需管理多个同步对象。
5. 锁与条件变量(Lock
+ Condition
)
通过条件唤醒机制实现顺序控制:
Lock lock = new ReentrantLock();
Condition condition1 = lock.newCondition();
Condition condition2 = lock.newCondition();
AtomicInteger flag = new AtomicInteger(1);
new Thread(() -> {
lock.lock();
try {
while (flag.get() != 1) condition1.await();
System.out.println("T1");
flag.set(2);
condition2.signal();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
lock.unlock();
}
}).start();
// T2、T3类似,通过判断flag和condition控制顺序
优点:底层控制能力强,适合复杂同步逻辑。
缺点:代码冗余,易出错,需处理锁的释放。
总结与选择建议
- 简单场景:优先选
Thread.join()
或单线程池。 - 异步任务编排:使用
CompletableFuture
。 - 复杂协作:
CountDownLatch
或CyclicBarrier
。 - 精准控制:锁与条件变量。
注意:多线程顺序执行通常与并发设计的初衷(提升效率)相悖,需确保业务场景确实需要顺序性,避免不必要的性能损失。