小伙伴们好呀,最近在重新复习,整理自己的知识库,偶然看到这道面试题:三个线程按顺序打印 ABCABC,尝试着做一下,才发现自己对线程还有好多地方不懂,蓝瘦…… 🐷
思路
很明显,这里就涉及线程间相互通信的知识了。
而相互通信的难点就是要控制好,阻塞和唤醒的时机。
一. 这里就是 A 通知 B,B 通知 C , C 通知 A
二. 三个线程在等待(阻塞)和唤醒(执行) 中不断切换。
三. 等待的方式大致分为两种
-
wait 方法 (Object native 方式 )
-
LockSupport.park 方式 ( Unsafe native 方式 )
四. 唤醒的方式
-
notify,notifyAll 方法 (Object native 方式 )
-
LockSupport.unPark 方式 ( Unsafe native 方式 )
五. 互斥条件
线程 A 先拿到资源 c,再拿资源 a ,[a 执行完后释放,并唤醒等待资源 a] 的 线程 B 线程 B 先拿到资源 a,再拿资源 b ,[b 执行完后释放,并唤醒等待资源 b] 的 线程 C 线程 C 先拿到资源 b,再拿资源 c ,[c 执行完后释放,并唤醒等待资源 c] 的 线程 A
所以得有 三个 共享资源 abc 来达到互斥条件
Synchronized 还是 ReentrantLock 都得建立 三个共享资源
六. 扩展
使用 LockSupport ,如果要像上面这样子的思路去解答,就得注意 线程相互引用行成的循环依赖问题,这里借用 Spring 的思路 用 Map 巧妙化解。
或者做法2 通过 外部的成员变量,不断地去判断,unpark 线程 a b c
Synchronized 方式
private static class MySynchronized {
void printABC() throws InterruptedException {
class MyRunable implements Runnable {
private Object lock1;
private Object lock2;
private CountDownLatch countDownLatch;
public MyRunable(Object lock1, Object lock2) {
this.lock1 = lock1;
this.lock2 = lock2;
}
public MyRunable(Object lock1, Object lock2, CountDownLatch countDownLatch) {
this.lock1 = lock1;
this.lock2 = lock2;
this.countDownLatch = countDownLatch;
}
@Override
public void run() {