在 Java 中,如果你要启动三个线程,它们分别打印 1、2、3,并且顺序执行(1 → 2 → 3)
,就不能直接用 Thread.start()
,因为线程是异步的,不能保证顺序。要想按顺序控制输出,必须使用线程间同步机制。
1. 使用 CountDownLatch 来控制线程执行顺序。
import java.util.concurrent.CountDownLatch;
public class MultiThreadPrint {
public static void main(String[] args) {
CountDownLatch latch2 = new CountDownLatch(1);
CountDownLatch latch3 = new CountDownLatch(1);
Thread t1 = new Thread(() -> {
System.out.println(1);
latch2.countDown();
}, "1");
Thread t2 = new Thread(() -> {
try {
latch2.await();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(2);
latch3.countDown();
}, "2");
Thread t3 = new Thread(() -> {
try {
latch3.await();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(3);
}, "3");
t3.start();
t2.start();
t1.start();
}
}
2. 信号量机制Semaphore
import java.util.concurrent.Semaphore;
public class SemaphoreExample {
public static void main(String[] args) {
Semaphore s1 = new Semaphore(1); // 先释放 1 → t1 可先执行
Semaphore s2 = new Semaphore(0); // t2 等待 t1 释放
Semaphore s3 = new Semaphore(0); // t3 等待 t2 释放
Thread t1 = new Thread(() -> {
try {
s1.acquire();
System.out.println("1");
s2.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
Thread t2 = new Thread(() -> {
try {
s2.acquire();
System.out.println("2");
s3.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
Thread t3 = new Thread(() -> {
try {
s3.acquire();
System.out.println("3");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
t3.start();
t2.start();
t1.start();
}
}
对比
特性 | CountDownLatch | Semaphore |
---|---|---|
控制方式 | 等待多个线程完成 | 控制并发线程数量 / 顺序 |
初始计数 | 固定值(如 3) | 可设置许可数(如 3) |
计数行为 | 只能 countDown() 递减 | acquire() 减、release() 增 |
能否重置 | 不可重置(用完即废) | 可重复使用 |
用途 | 一次性等待多个线程完成后再执行某个操作 | 控制资源访问、限流、按序执行 |
阻塞方法 | await() (等待倒计数归零) | acquire() (获取许可) |
唤醒方式 | countDown() 到 0 自动唤醒所有等待线程 | release() 主动释放许可,唤醒一个/多个线程 |
实现原理 | 基于 AQS 的 共享模式(等待计数归 0) | 基于 AQS 的 共享模式(管理许可数量) |