参考资料
华为和阿里都考过的多线程编程题,你会吗?多线程交替打印 ABC的多种实现方法
八个经典的java多线程编程题目
例题
编写一个程序,开启三个线程,这三个线程的 ID 分别是 A、B 和 C,
- 每个线程把自己的 ID 在屏幕上打印 10 遍,
- 要求输出结果必须按 ABC 的顺序显示,如 ABCABCABC… 依次递推
解法一 LockSupport
使用LockSupport
类的park()
和unpark()
方法,可以唤醒指定的线程。
- 新建三个线程A,B,C
- A线程打印后,调用unpark唤醒B,然后调用park阻塞自己。
- B线程先调用park阻塞自己,被A线程唤醒后,打印B,然后调用unpark唤醒C
- C线程先调用park阻塞自己,被B线程唤醒后,打印C,然后调用unpark唤醒A
- 重复10次
public class PrintABC {
static Thread threadA,threadB, threadC;
public static void main(String[] args) {
threadA = new Thread(() -> {
for (int i = 0; i < 10; i++) {
System.out.print(Thread.currentThread().getName());
// 唤醒下一个线程
LockSupport.unpark(threadB);
// 阻塞自己
LockSupport.park();
}
}, "A");
threadB = new Thread(() -> {
for (int i = 0; i < 10; i++) {
// 阻塞自己
LockSupport.park();
System.out.print(Thread.currentThread().getName());
// 唤醒下一个线程
LockSupport.unpark(threadC);
}
}, "B");
threadC = new Thread(() -> {
for (int i = 0; i < 10; i++) {
// 阻塞自己
LockSupport.park();
System.out.print(Thread.currentThread().getName());
// 唤醒下一个线程
LockSupport.unpark(threadA);
}
}, "C");
threadA.start();
threadB.start();
threadC.start();
}
}
解法二 synchronized wait nofifyall
使用synchronize
配合wait
和nofifyAll
方法,notify方法无法唤醒指定的线程,因此,我们需要额外的变量辅助判断。这里使用一个静态变量count
记录打印数字的次数,并且为每一个线程赋予一个id
,如果没有轮到线程打印,调用wait方法进入等待状态,释放锁,同时使用notify方法通知其他线程从wait状态中唤醒,去重新竞争锁。
public class PrintABC2 extends Thread {
private static int count = 0;
private static int number = 10;
private Object lock;
private int id;
public PrintABC2(Object lock, int id, String name) {
super(name);
this.lock = lock;
this.id = id;
}
@Override
public void run() {
int i = 0;
synchronized (lock) {
while (i < number) {
while (count % 3 != id) {
lock.notifyAll();// 通知其他线程从wait状态中醒过来,重新竞争锁
try {
// 没有轮到自己打印,线程进入等待状态,放弃对象锁
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}