多线程顺序输出abc
在面试中,如果你能:
先给出经典的 ReentrantLock + Condition 方案,展示你扎实的基础。
然后提出单线程池方案,展示你巧妙的工程思维。
最后再分析 LockSupport 方案,展示你对JUC底层工具的深度理解。
方案1:经典的 ReentrantLock + Condition
public class ElegantPrintABCWithCondition {
private final Lock lock = new ReentrantLock();
private final int totalRounds;
private Condition[] conditions = { lock.newCondition(), lock.newCondition(), lock.newCondition() };
// 使用 volatile 保证 state 在多线程间的可见性
private volatile int state = 0;
public ElegantPrintABCWithCondition(int totalRounds) {
this.totalRounds = totalRounds;
}
public void print(int threadId, char charToPrint) {
for (int i = 0; i < totalRounds; i++) {
lock.lock();
try {
// 使用 while 循环判断,防止虚假唤醒
while (state % 3 != threadId) {
// 等待属于自己的 Condition
conditions[threadId].await();
}
// 轮到自己,执行打印
System.out.print(charToPrint);
// 状态递增
state++;
// 按顺序唤醒下一个线程,(threadId + 1) % 3 实现了 0->1, 1->2, 2->0 的循环
conditions[(threadId + 1) % 3].signal();
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // 保持中断状态
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
public static void main(String[] args) {
ElegantPrintABCWithCondition printer = new ElegantPrintABCWithCondition(10);
// 通过 Lambda 表达式为每个线程分配不同的任务
new Thread(() -> printer.print(0, 'A')).start();
new Thread(() -> printer.print(1, 'B')).start();
new Thread(() -> printer.print(2, 'C')).start();
}
}
方案2:单线程池方案
public class ElegantPrintABC {
public static void main(String[] args) {
// 创建一个单线程的 ExecutorService
ExecutorService executor = Executors.newSingleThreadExecutor();
// 定义打印的总轮数
final int totalRounds = 10;
for (int i = 0; i < totalRounds; i++) {
// 按顺序提交 A, B, C 三个任务
executor.submit(() -> System.out.print("A"));
executor.submit(() -> System.out.print("B"));
executor.submit(() -> System.out.print("C"));
}
// 关闭线程池
executor.shutdown();
}
}
方案3 :使用 `LockSupport.park/unpark`——更底层的精准控制
public class ElegantPrintABCWithLockSupport {
static List<Thread> threads = new ArrayList<>();
public static void main(String[] args) {
final int totalRounds = 10;
char[] chars = {'A', 'B', 'C'};
for (int i = 0; i < chars.length; i++) {
final int index = i;
Thread t = new Thread(() -> {
for (int j = 0; j < totalRounds; j++) {
// 等待被前一个线程 unpark
LockSupport.park();
System.out.print(chars[index]);
// 计算下一个要唤醒的线程的索引
int nextIndex = (index + 1) % threads.size();
// unpark 下一个线程
LockSupport.unpark(threads.get(nextIndex));
}
});
threads.add(t);
}
// 启动所有线程
for (Thread t : threads) {
t.start();
}
// 启动第一个线程 A
LockSupport.unpark(threads.get(0));
}
}