5、多线程交替顺序打印ABC的多种方式
面试题:有 3 个独立的线程,一个只会输出 A,一个只会输出 B,一个只会输出 C,在三个线程启动的情况下,请用合理的方式让他们按顺序打印 ABC。
使用join
join() 的核心作用是:让当前线程(通常是主线程)等待调用 join() 的那个线程执行完毕后再继续执行。
以下是具体的代码实现和原理解析:
public class JoinPrintABC {
public static void main(String[] args) throws InterruptedException {
// 1. 创建三个线程,分别负责打印 A、B、C
Thread threadA = new Thread(() -> System.out.print("A"));
Thread threadB = new Thread(() -> System.out.print("B"));
Thread threadC = new Thread(() -> System.out.print("C"));
// 2. 启动线程 A
threadA.start();
// 3. 主线程等待 A 执行完,才往下走
threadA.join();
// 4. 启动线程 B (此时 A 肯定已经打完了)
threadB.start();
// 5. 主线程等待 B 执行完
threadB.join();
// 6. 启动线程 C (此时 B 肯定已经打完了)
threadC.start();
// 7. 主线程等待 C 执行完
threadC.join();
System.out.println("\n完成");
}
}进阶:如果面试官问“能不能不依赖主线程控制顺序?”
上面的解法是主线程在指挥顺序。如果面试官要求“三个线程一旦启动,它们自己内部协调顺序”(即主线程启动完就不管了),那么就不能单纯用上面的 main 方法里的 join 写法,而是要在线程内部互相 join。
// 这种写法下,主线程可以一次性把 t1, t2, t3 都 start 掉
Thread t1 = new Thread(() -> System.out.print("A"));
Thread t2 = new Thread(() -> {
try { t1.join(); } catch (InterruptedException e) {} // t2 等待 t1
System.out.print("B");
});
Thread t3 = new Thread(() -> {
try { t2.join(); } catch (InterruptedException e) {} // t3 等待 t2
System.out.print("C");
});
t1.start();
t2.start();
t3.start();
// 输出结果依然是 ABC但对于大多数基础面试题,“在主线程中依次 start 和 join” 是最标准、最容易得分的答案。
但是这种方式只适合 打印一次ABC。
因为 join() 会阻塞当前流程,如果你要循环打印(比如打印 10 次 ABC),这种写法就需要配合复杂的循环和多次创建线程(或者在循环内反复 join),效率较低且代码冗余。
如果要执行多次打印,可以看下面的方案
使用lock,Condition
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ABC {
//可重入锁
private final static Lock lock = new ReentrantLock();
//判断是否执行:1表示应该A执行,2表示应该B执行,3表示应该C执行
private static int state = 1;
//condition对象
private static Condition a = lock.newCondition();
private static Condition b = lock.newCondition();
private static Condition c = lock.newCondition();
public static void printA() {
//通过循环,hang住线程
for (int i = 0; i < 10; i++) {
try {
//获取锁
lock.lock();
//并发情况下,不能用if,要用循环判断等待条件,避免虚假唤醒
while (state != 1) {
a.await();
}
System.out.print("A");
state = 2;
b.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//要保证不执行的时候,锁能释放掉
lock.unlock();
}
}
}
public static void printB() throws InterruptedException {
for (int i = 0; i < 10; i++) {
try {
lock.lock();
//获取到锁,应该执行
while (state != 2) {
b.await();
}
System.out.print("B");
state = 3;
c.signal();
} finally {
lock.unlock();
}
}
}
public static void printC() throws InterruptedException {
for (int i = 0; i < 10; i++) {
try {
lock.lock();
while (state != 3) {
c.await();
}
//获取到锁,应该执行
System.out.print("C");
state = 1;
a.signal();
} finally {
lock.unlock();
}
}
}
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
ABC.printA();
}
}, "A").start();
new Thread(new Runnable() {
@Override
public void run() {
try {
ABC.printB();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "B").start();
new Thread(new Runnable() {
@Override
public void run() {
try {
ABC.printC();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "C").start();
}
}使用AtomicInteger
import java.util.concurrent.atomic.AtomicInteger;
public class ABC2 {
private static AtomicInteger state = new AtomicInteger(1);
public static void printA() {
for (int i = 0; i < 10; i++) {
while (true) {
if (state.get() == 1) {
System.out.print("A");
state.incrementAndGet();
break;
}
}
}
}
public static void printB() {
for (int i = 0; i < 10; i++) {
while (true) {
if (state.get() == 2) {
System.out.print("B");
state.incrementAndGet();
break;
}
}
}
}
public static void printC() {
for (int i = 0; i < 10; i++) {
while (true) {
if (state.get() == 3) {
System.out.print("C");
state.set(1);
break;
}
}
}
}
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
ABC2.printA();
}
}, "A").start();
new Thread(new Runnable() {
@Override
public void run() {
ABC2.printB();
}
}, "B").start();
new Thread(new Runnable() {
@Override
public void run() {
ABC2.printC();
}
}, "C").start();
}
}使用LockSupprt
public class ABC3 {
private static Thread t1, t2, t3;
public static void main(String[] args) {
t1 = new Thread(() -> {
for (int i = 0; i < 2; i++) {
LockSupport.park();
System.out.print("A");
LockSupport.unpark(t2);
}
});
t2 = new Thread(() -> {
for (int i = 0; i < 2; i++) {
LockSupport.park();
System.out.print("B");
LockSupport.unpark(t3);
}
});
t3 = new Thread(() -> {
for (int i = 0; i < 2; i++) {
LockSupport.park();
System.out.print("C");
LockSupport.unpark(t1);
}
});
t1.start();
t2.start();
t3.start();
// 主线程稍微等待一下,确保其他线程已经启动并且进入park状态。
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 启动整个流程
LockSupport.unpark(t1);
}
}
