算法-併發

生產者-消費者

Condition

BlockingQueue

LeetCode:1114. 按序打印

LeetCode:1114. 按序打印

三個不同的線程調用同一個對象中的三個不同的方法,要求必須按照 first() --> second() --> third() 的調用順序進行調用;
這道題有點類似於程序執行順序的編排,即:first() 方法先執行,調用對應線程的 run() 方法;然後second() 方法執行,調用對應線程的 run() 方法;最後 third() 方法執行,調用對應線程的 run() 方法;
全程只要求三個方法的調用順序必須爲 first() --> second() --> third(),並不要求線程的調用順序,因此這裏的三個線程實際爲干擾信息;這裏哪怕就是一個線程的執行體,但是保證 first() --> second() --> third() 的方法調用順序,程序的執行結果也是正確的。
因此只需要在 first()、second()、third() 三個方法間建立屏障,使程序的執行始終滿足如下順序即可:

  1. first() 方法先執行,first() 方法執行完畢後,通知 second() 方法執行;
  2. second() 方法等待 first() 方法執行完畢,在 first() 方法執行完畢後,開始執行 second() 方法,second() 方法執行完畢後,通知 third() 方法執行;
  3. third() 方法等待 second() 方法執行完畢,在 second() 方法執行完畢後,開始執行 third() 方法,third() 方法執行完畢後,程序執行完成並退出;

[2]解法一:無鎖

利用 volatile 的可見性,設置並更新屏障值,並利用對應值來作爲各方法之間的屏障;
non_lock

參考文檔

玩轉Leetcode多線程——JAVA線程協助工具類實戰

[3]解法二:同步-synchronized

使用 boolean 類型變量作爲 first() 與 second()、second() 與 third() 方法之間的屏障,通過在 synchronizd 加鎖的代碼中更新 boolean 類型變量來更新標誌量,通過 wait() 方法來使當前線程進入等待狀態,通過 notifyAll() 方法來喚醒所有正在等待中的線程繼續執行;整體的代碼執行流程可以歸納爲:加鎖 --> 等待 --> 執行 --> 置標誌 --> 通知,其中"加鎖"和"執行"爲必須步驟,其他步驟隨功能不同而可選;
synchronize

參考文檔

構造執行屏障實現

[3]解法三:同步-Condition

使用 Condition 對象來作爲 first() 與 second()、second() 與 third() 方法之間的屏障,通過 Conditon 對象的 await()/signal() 方法來完成兩個線程之間狀態的同步
condition

參考文檔

玩轉Leetcode多線程——JAVA線程協助工具類實戰

[1]解法四:CountDownLatch

使用 CountDownLatch 作爲 first() 與 second()、second() 與 third() 方法之間的屏障;設置 CountDownLatch 的 count 初始值爲 1,當一個方法執行完畢後,通過 countDown() 方法使 CountDownLatch 的 count 計數減 1,進而達到通知另一個方法執行的目的;通過 await() 方法等待 count 計數變爲 0,以此表示前置方法執行完畢,當前方法可以開始執行;
count_down_latch

參考文檔

countDownLatch計數器解題詳細思路

[1]解法五:CyclicBarrier

使用 CyclicBarrier 作爲 first() 與 second()、second() 與 third() 之間的屏障;設置屏障的初始值爲2,await() 方法用於等待所有線程都到達屏障再執行
cyclic_barrier

參考文檔

玩轉Leetcode多線程——JAVA線程協助工具類實戰

[2]解法六:Semaphore

使用 Semaphore 作爲 first() 與 second()、second() 與 third() 之間的屏障;這裏使用了 Semaphore 的一個特殊用法:Semaphore(0),這種情況下,只有先 release,才能 acquire,很適合作爲方法見的屏障
semaphore

參考文檔

玩轉Leetcode多線程——JAVA線程協助工具類實戰

[2]解法七:AtomicInteger

使用 AtomicInteger 作爲 first() 與 second()、second() 與 third() 方法之間的屏障,通過 incrementAndGet() 方法來更新屏障值,通過 get() 方法來獲取屏障值並做出對應處理;
atomic_integer

參考文檔

按序打印

LeetCode:1115. 交替打印FooBar

LeetCode:1115. 交替打印FooBar

解法一:[1]無鎖

無鎖的情況下,主要使用 volatile 關鍵字保證線程的可見性;
設計一個使用 volatile 修飾的變量 permitFoo:
當允許打印 Foo 時,將該變量設置爲 true,然後打印 Foo 並且更新該變量的狀態;
當允許打印 Bar 時,將該變量設置爲 false,然後打印 Bar 並且更新該變量的狀態;
non_lock

參考文檔

玩轉Leetcode多線程——JAVA線程協助工具類實戰

解法二:[3]ReentrantLock

使用 ReentrantLock 構造可重入鎖、公平鎖,用於保證兩個線程間的狀態同步;

參考文檔

玩轉Leetcode多線程——JAVA線程協助工具類實戰

解法三:[3]Semaphore

參考文檔

玩轉Leetcode多線程——JAVA線程協助工具類實戰

解法四:[2]CyclicBarrier

參考文檔

玩轉Leetcode多線程——JAVA線程協助工具類實戰

解法五:[2]Condition

Condition,不用於 synchronized 的另一種更高效的實現線程同步的方式;
condition

參考文檔

利用ReentrantLock和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

參考文檔

Java 使用阻塞隊列來控制

LeetCode:1116. 打印零與奇偶數

解法一:Semaphore

使用信號量在三個線程之間建立屏障。
當輸出0的線程獲取信號量後,進行0的輸出,然後判斷下一個將要進行輸出的數字;
	若爲奇數,則釋放輸出奇數的信號量,輸出奇數的線程獲取信號量,進行輸出,然後釋放輸出0的信號量;
	若爲偶數,則釋放輸出偶數的信號量,輸出偶數的線程獲取信號量,進行輸出,然後釋放輸出0的信號量;

參考代碼:

解法二:Lock

解法三:synchronized

解法四:無鎖

相關問題

IntCustomer的作用與用法?

參考文檔

JAVA併發工具類大練兵

LeetCode:1195. 交替打印字符串

LeetCode:1195. 交替打印字符串

相關問題

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章