記一個有意思的面試題 → 執行緒交替輸出問題
開心一刻
大年初一,一起嗨起來!!!
強調一句很重要的心裡話:祝大家在 2021 年,健康好運,平安幸福!
問題描述
用兩個執行緒,一個輸出數字,一個輸出字母,交替輸出 1A2B3C4D...26Z
該如何實現?
解決方式
據說解決方式有上百種,但有些是脫了褲子放屁,有些是民間偏方,所以沒必要全部都知道(其實樓主也不知道具體是哪一百多種)
掌握常用的那幾個就好;為了方便,我們就以 1234567 和 ABCDEFG 為例進行演示
synchronized + wait + notify
如果我們對 JUC 不熟的話,那這種方式往往是我們最容易想到的
這種方式,相信大家都能寫出來,但是這裡留三個問題(面試點)
1、執行緒程式碼中, try 中的 notify() 能否與 wait() 交換位置,為什麼
2、執行緒程式碼中, for 下的 notify() 能否去掉,為什麼
3、上面的程式碼能否保證一定先輸出數字,為什麼,如何保證一定先輸出數字
ReentrantLock + Condition + await + signal
很多場景下,用 ReentrantLock 可以替代 synchronized ,而在交叉輸出這個場景中,同樣可以替代
這種方式,寫出來應該也不難,同樣留三個問題(面試點)
1、執行緒程式碼中, for 中的 signal() 能否與 await() 交換位置,為什麼
2、執行緒程式碼中, for 下的 signal() 能否去掉,為什麼
3、上面的程式碼能否保證一定先輸出數字,為什麼,如何保證一定先輸出數字
LockSupport + park + unpark
估計很多人都沒想到這種方式,直接上程式碼
這是目前最優的解決方式,照樣留四個問題(面試點)
1、 t1.start() 能否與 t2.start() 交換位置,為什麼
2、執行緒 t1 中的 LockSupport.unpark(t2) 線上程 t2 中的 LockSupport.park() 之前執行會怎麼樣,為什麼
3、上面的程式碼能否保證一定先輸出數字,為什麼
4、 LockSupport 的 park 、 unpark 與 Object 的 wait 、 notify 有什麼異同
CAS
這種方式可能也比較難想到,直接上程式碼
這種方式也許不太好理解,留四個問題(面試點)加深理解
1、執行緒程式碼中, while 條件為什麼是 !=,而不是 ==
2、上面的程式碼能否保證一定先輸出數字,為什麼
3、CAS 的優缺點是什麼,適用於什麼場景
CAS + AtomicInteger
其實就是 CAS 的一個變種,直接上程式碼
CAS + AtomicReference
也是 CAS 的一個變種,直接上程式碼
TransferQueue
一般很難想象到這種方式,但卻是很有趣的一種實現方式
如果不瞭解 TransferQueue ,那這種方式就想不到;同樣留一個問題(面試點)
1、上面的程式碼能否保證一定先輸出數字,為什麼
BlockingQueue
一般也比較難想到這種方式,有所瞭解就好
PipedStream
效率很低,知道有這麼回事就好
總結
1、示例程式碼地址:juc-demo
2、需要掌握的實現方式
synchronized、ReentrantLock、LockSupport、CAS、TransferQueue 這幾種實現方式必須掌握
其他的瞭解就好
3、如何保證一定先輸出數字
上面介紹的那些方式中,有些是不能保證一定先輸出數字的,而有些是能保證一定先輸出數字的
不能保證先輸出數字的,可以用 CountDownLatch 來控制,是一種比較理想的