"三个线程按顺序打印"是一个经典的并发编程面试题,通常要求线程 A 打印 1,线程 B 打印 2,线程 C 打印 3,然后循环往复。
实现这个逻辑的核心在于线程间的通信(Signaling) 。我们可以使用 Java 的 synchronized 配合 wait/notify,或者更现代的 ReentrantLock 配合 Condition。
方案一:使用 ReentrantLock 和 Condition(推荐)
这是最清晰的方案。我们为每个线程创建一个"信号灯"(Condition),手动控制唤醒哪一个。
java
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class SequencePrint {
private final Lock lock = new ReentrantLock();
private final Condition cond1 = lock.newCondition();
private final Condition cond2 = lock.newCondition();
private final Condition cond3 = lock.newCondition();
private int state = 1; // 当前应该打印的序号
public void print(String name, int targetState, Condition current, Condition next) {
for (int i = 0; i < 10; i++) { // 假设打印10轮
lock.lock();
try {
// 如果还没轮到我,就一直等着
while (state != targetState) {
current.await();
}
System.out.print(name + " ");
// 修改状态并唤醒下一个线程
state = (targetState % 3) + 1;
next.signal();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
lock.unlock();
}
}
}
public static void main(String[] args) {
SequencePrint sp = new SequencePrint();
new Thread(() -> sp.print("A", 1, sp.cond1, sp.cond2)).start();
new Thread(() -> sp.print("B", 2, sp.cond2, sp.cond3)).start();
new Thread(() -> sp.print("C", 3, sp.cond3, sp.cond1)).start();
}
}
方案二:使用 synchronized 和 wait/notifyAll(最基础)
这种方式代码最少,但效率略低,因为 notifyAll() 会唤醒所有等待线程,不满足条件的线程会被唤醒后再次检查并挂起。
java
public class WaitNotifyPrint {
private int state = 1;
private final Object lock = new Object();
public void print(String name, int targetState) {
for (int i = 0; i < 10; i++) {
synchronized (lock) {
while (state != targetState) {
try { lock.wait(); } catch (InterruptedException e) {}
}
System.out.print(name);
state = (state % 3) + 1;
lock.notifyAll(); // 唤醒所有人,让他们竞争判断 state
}
}
}
public static void main(String[] args) {
WaitNotifyPrint wnp = new WaitNotifyPrint();
new Thread(() -> wnp.print("A", 1)).start();
new Thread(() -> wnp.print("B", 2)).start();
new Thread(() -> wnp.print("C", 3)).start();
}
}