算法-併發
- 生產者-消費者
- LeetCode:1114. 按序打印
- [2]解法一:無鎖
- [3]解法二:同步-synchronized
- [3]解法三:同步-Condition
- [1]解法四:CountDownLatch
- [1]解法五:CyclicBarrier
- [2]解法六:Semaphore
- [2]解法七:AtomicInteger
- LeetCode:1115. 交替打印FooBar
- 解法一:[1]無鎖
- 解法二:[3]ReentrantLock
- 解法三:[3]Semaphore
- 解法四:[2]CyclicBarrier
- 解法五:[2]Condition
- 解法六:[1]synchronizd
- 解法七:[1]阻塞隊列
- LeetCode:1116. 打印零與奇偶數
- LeetCode:1195. 交替打印字符串
生產者-消費者
Condition
BlockingQueue
LeetCode:1114. 按序打印
三個不同的線程調用同一個對象中的三個不同的方法,要求必須按照 first() --> second() --> third() 的調用順序進行調用;
這道題有點類似於程序執行順序的編排,即:first() 方法先執行,調用對應線程的 run() 方法;然後second() 方法執行,調用對應線程的 run() 方法;最後 third() 方法執行,調用對應線程的 run() 方法;
全程只要求三個方法的調用順序必須爲 first() --> second() --> third(),並不要求線程的調用順序,因此這裏的三個線程實際爲干擾信息;這裏哪怕就是一個線程的執行體,但是保證 first() --> second() --> third() 的方法調用順序,程序的執行結果也是正確的。
因此只需要在 first()、second()、third() 三個方法間建立屏障,使程序的執行始終滿足如下順序即可:
- first() 方法先執行,first() 方法執行完畢後,通知 second() 方法執行;
- second() 方法等待 first() 方法執行完畢,在 first() 方法執行完畢後,開始執行 second() 方法,second() 方法執行完畢後,通知 third() 方法執行;
- third() 方法等待 second() 方法執行完畢,在 second() 方法執行完畢後,開始執行 third() 方法,third() 方法執行完畢後,程序執行完成並退出;
[2]解法一:無鎖
利用 volatile 的可見性,設置並更新屏障值,並利用對應值來作爲各方法之間的屏障;
non_lock
參考文檔
[3]解法二:同步-synchronized
使用 boolean 類型變量作爲 first() 與 second()、second() 與 third() 方法之間的屏障,通過在 synchronizd 加鎖的代碼中更新 boolean 類型變量來更新標誌量,通過 wait() 方法來使當前線程進入等待狀態,通過 notifyAll() 方法來喚醒所有正在等待中的線程繼續執行;整體的代碼執行流程可以歸納爲:加鎖 --> 等待 --> 執行 --> 置標誌 --> 通知,其中"加鎖"和"執行"爲必須步驟,其他步驟隨功能不同而可選;
synchronize
參考文檔
[3]解法三:同步-Condition
使用 Condition 對象來作爲 first() 與 second()、second() 與 third() 方法之間的屏障,通過 Conditon 對象的 await()/signal() 方法來完成兩個線程之間狀態的同步
condition
參考文檔
[1]解法四:CountDownLatch
使用 CountDownLatch 作爲 first() 與 second()、second() 與 third() 方法之間的屏障;設置 CountDownLatch 的 count 初始值爲 1,當一個方法執行完畢後,通過 countDown() 方法使 CountDownLatch 的 count 計數減 1,進而達到通知另一個方法執行的目的;通過 await() 方法等待 count 計數變爲 0,以此表示前置方法執行完畢,當前方法可以開始執行;
count_down_latch
參考文檔
[1]解法五:CyclicBarrier
使用 CyclicBarrier 作爲 first() 與 second()、second() 與 third() 之間的屏障;設置屏障的初始值爲2,await() 方法用於等待所有線程都到達屏障再執行
cyclic_barrier
參考文檔
[2]解法六:Semaphore
使用 Semaphore 作爲 first() 與 second()、second() 與 third() 之間的屏障;這裏使用了 Semaphore 的一個特殊用法:Semaphore(0),這種情況下,只有先 release,才能 acquire,很適合作爲方法見的屏障
semaphore
參考文檔
[2]解法七:AtomicInteger
使用 AtomicInteger 作爲 first() 與 second()、second() 與 third() 方法之間的屏障,通過 incrementAndGet() 方法來更新屏障值,通過 get() 方法來獲取屏障值並做出對應處理;
atomic_integer
參考文檔
LeetCode:1115. 交替打印FooBar
解法一:[1]無鎖
無鎖的情況下,主要使用 volatile 關鍵字保證線程的可見性;
設計一個使用 volatile 修飾的變量 permitFoo:
當允許打印 Foo 時,將該變量設置爲 true,然後打印 Foo 並且更新該變量的狀態;
當允許打印 Bar 時,將該變量設置爲 false,然後打印 Bar 並且更新該變量的狀態;
non_lock
參考文檔
解法二:[3]ReentrantLock
使用 ReentrantLock 構造可重入鎖、公平鎖,用於保證兩個線程間的狀態同步;
參考文檔
解法三:[3]Semaphore
參考文檔
解法四:[2]CyclicBarrier
參考文檔
解法五:[2]Condition
Condition,不用於 synchronized 的另一種更高效的實現線程同步的方式;
condition
參考文檔
解法六:[1]synchronizd
synchronized 通常與 Object 鎖配合使用;
設計一個變量 fooTurn,該變量用於標識是否可進行 Foo 的打印;
當允許打印 Foo 時,進行 Foo 的打印,更新 fooTurn 的狀態並通知其他線程繼續執行;
當允許打印 Bar 時,進行 Bar 的打印,更新 fooTurn 的狀態並通知其他線程繼續執行;
synchronized
參考文檔
java用synchronized關鍵字寫和用Boolean判斷+完整測試代碼 --交替打印foobar
解法七:[1]阻塞隊列
交替打印 Foo 與 Bar 的線程模型,實際上符合生產者-消費者的線程模型,因此可以使用阻塞隊列來實現;
設計一個容量爲 1 的阻塞隊列,向阻塞隊列中添加一個元素,然後進行 Foo 的打印,然後再次向阻塞隊列中添加元素,此時當前線程則會阻塞,必須等待另一個線程從隊列中取出元素;同理,從隊列中取出一個元素,然後打印 Bar,然後再次從阻塞隊列中取出元素,此時當前線程會被阻塞,必須等待另一個線程向阻塞隊列中添加元素;
blockingqueue
參考文檔
LeetCode:1116. 打印零與奇偶數
解法一:Semaphore
使用信號量在三個線程之間建立屏障。
當輸出0的線程獲取信號量後,進行0的輸出,然後判斷下一個將要進行輸出的數字;
若爲奇數,則釋放輸出奇數的信號量,輸出奇數的線程獲取信號量,進行輸出,然後釋放輸出0的信號量;
若爲偶數,則釋放輸出偶數的信號量,輸出偶數的線程獲取信號量,進行輸出,然後釋放輸出0的信號量;
參考代碼: