Java 多线程算法每日一题:多线程交替打印 ABC
题目描述
编写一个 Java 程序,创建 3 个线程,分别负责打印字符 A、B、C,要求 3 个线程协同工作,按 “A→B→C” 的顺序循环打印 10 次,最终输出结果为 “ABCABCABC...ABC”(共 30 个字符,循环 10 轮)。
核心要求:
- 严格保证打印顺序,不可出现 “ABA”“ACB” 等乱序情况;
- 线程间需协调同步,避免资源竞争导致的打印混乱;
- 循环结束后,所有线程需正常终止,无资源泄漏。
解题思路
多线程交替打印的核心是 “线程间的有序通信”—— 每个线程打印完成后,需通知下一个线程开始打印,同时自身进入等待状态。常见两种实现方案:
- 基于 ReentrantLock+Condition:利用 Lock 锁保证临界区互斥,通过 Condition 的await()/signal()实现线程间精准唤醒,灵活性高;
- 基于 Object 的 wait ()/notify ():利用 Java 原生的对象监视器锁(synchronized),通过wait()让线程等待,notify()唤醒指定线程,无需额外引入 Lock 类。
两种方案均需维护一个 “当前应打印的字符标识”,线程打印前需检查标识是否匹配,不匹配则等待,匹配则打印并更新标识,再唤醒下一个线程。
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
// 打印控制器:封装锁、条件变量与打印状态
class PrintController {
// 锁对象:保证打印操作的互斥性
private final ReentrantLock lock = new ReentrantLock();
// 3个条件变量:分别对应A、B、C线程的等待/唤醒
private final Condition conditionA = lock.newCondition();
private final Condition conditionB = lock.newCondition();
private final Condition conditionC = lock.newCondition();
// 当前应打印的字符(初始为'A',表示第一个打印A)
private char currentChar = 'A';
// 已完成的打印轮次(控制循环10次)
private int round = 0;
// A线程的打印方法
public void printA() throws InterruptedException {
// 加锁:进入临界区,确保线程安全
lock.lock();
try {
// 循环检查:若当前不应打印A,或已完成10轮,则A线程等待
while (currentChar != 'A' || round >= 10) {
conditionA.await(); // A线程进入等待,释放锁
}
// 打印逻辑:输出A
System.out.print("A");
// 更新状态:下一个应打印B,完成1轮(每打印C后才算1轮,此处仅标记A完成)
currentChar = 'B';
// 唤醒B线程:通知B可以开始打印
conditionB.signal();
} finally {
// 解锁:无论是否发生异常,都需释放锁,避免死锁
lock.unlock();
}
}
// B线程的打印方法(逻辑与A类似)
public void printB() throws InterruptedException {
lock.lock();
try {
while (currentChar != 'B' || round >= 10) {
conditionB.await();
}
System.out.print("B");
currentChar = 'C'; // 下一个应打印C
conditionC.signal(); // 唤醒C线程
} finally {
lock.unlock();
}
}
// C线程的打印方法(需额外处理轮次计数)
public void printC() throws InterruptedException {
lock.lock();
try {
while (currentChar != 'C' || round >= 10) {
conditionC.await();
}
System.out.print("C");
currentChar = 'A'; // 下一轮重新从A开始
round++; // 每打印完C,代表1轮完成,轮次+1
// 唤醒A线程:通知下一轮A可以开始打印
conditionA.signal();
} finally {
lock.unlock();
}
}
}
// 线程任务:实现Runnable,封装具体打印逻辑
class PrintTask implements Runnable {
private final PrintController controller;
private final char taskChar; // 当前任务需打印的字符(A/B/C)
public PrintTask(PrintController controller, char taskChar) {
this.controller = controller;
this.taskChar = taskChar;
}
@Override
public void run() {
try {
// 循环打印:直到完成10轮(round >= 10时,打印方法会等待,需退出循环)
while (controller.round < 10) {
// 根据任务字符调用对应打印方法
switch (taskChar) {
case 'A':
controller.printA();
break;
case 'B':
controller.printB();
break;
case 'C':
controller.printC();
break;
}
// 轻微休眠:避免线程过度抢占CPU(可选,不影响正确性)
Thread.sleep(10);
}
} catch (InterruptedException e) {
// 捕获中断异常,恢复线程中断状态(规范操作)
Thread.currentThread().interrupt();
}
}
}
// 测试类:创建线程并启动
public class AlternatePrintABC {
public static void main(String[] args) {
// 1. 创建打印控制器(共享资源)
PrintController controller = new PrintController();
// 2. 创建3个线程,分别绑定A、B、C打印任务
Thread threadA = new Thread(new PrintTask(controller, 'A'), "Thread-A");
Thread threadB = new Thread(new PrintTask(controller, 'B'), "Thread-B");
Thread threadC = new Thread(new PrintTask(controller, 'C'), "Thread-C");
// 3. 启动线程(启动顺序不影响最终打印顺序,因控制器会控制等待/唤醒)
threadA.start();
threadB.start();
threadC.start();
// 4. 主线程等待子线程结束(可选,确保程序完整执行)
try {
threadA.join();
threadB.join();
threadC.join();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println("\n10轮打印完成!");
}
}
更多推荐


所有评论(0)