这篇文章分享一道非常不错的题目:三个线程按序打印ABC。
很多读者朋友应该都觉得这道题目不难,这次给大家带来十二种做法,一定有你没有见过的新姿势。
1. synchronized+wait+notify
说到同步,我们很容易就想到synchronized。
线程间通信呢?我们先回忆一下线程间的调度。
可以看到,等待和运行之间的转换可以用wait和notify。
那么整体思路也就有了:
- 打印的时候需要获取锁
- 打印B的线程需要等待打印A线程执行完,打印C的线程需要等待打印B线程执行完
- 代码
public class ABC1 {
//锁住的对象
private final static Object lock = new Object();
//A是否已经执行
private static boolean aExecuted = false;
//B是否已经执行过
private static boolean bExecuted = false;
public static void printA() {
synchronized (lock) {
System.out.println("A");
aExecuted = true;
//唤醒所有等待线程
lock.notifyAll();
}
}
public static void printB() throws InterruptedException {
synchronized (lock) {
//获取到锁,但是要等A执行
while (!aExecuted) {
lock.wait();
}
System.out.println("B");
bExecuted = true;
lock.notifyAll();
}
}
public static void printC() throws InterruptedException {
synchronized (lock) {
//获取到锁,但是要等B执行
while (!bExecuted) {
lock.wait();
}
System.out.println("C");
}
}
}
复制代码
- 测试:后面几种方法的单测基本和这种方法一致,所以后面的单测就省略了。
@Test
void abc1() {
//线程A
new Thread(() -> {
ABC1.printA();
}, "A").start();
//线程B
new Thread(() -> {
try {
ABC1.printB();
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "B").start();
//线程C
new Thread(() -> {
try {
ABC1.printC();
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "C").start();
}
复制代码
2. lock+全局变量state
还可以用lock+state来实现,大概思路:
- 用lock来实现同步
- 用全局变量state标识改哪个线程执行,不执行就释放锁
- 代码
public class ABC2 {
//可重入锁
private final static Lock lock = new ReentrantLock();
//判断是否执行:1表示应该A执行,2表示应该B执行,3表示应该C执行
private static int state = 1;
public static void printA() {
//自旋
while (state < 4) {
try {
//获取锁
lock.lock();
//并发情况下,不能用if,要用循环判断等待条件,避免虚假唤醒
while (state == 1) {
System.out.println("A");
state++;
}
} finally {
//要保证不执行的时候,锁能释放掉
lock.unlock();
}
}
}
public static void printB() throws InterruptedException {
while (state < 4) {
try {
lock.lock();
//获取到锁,应该执行
while (state == 2) {
System.out.println("B");
state++;
}
} finally {
lock.unlock();
}
}
}