最近使用Condition時有一些疑惑,于是自己做了幾個實驗,了解了Condition的具體用法,現記錄如下。
首先我們直接來看一段之前網上看到的一個輸出1-9的例子,當然網上那個例子是有缺陷的,并不能保證每次都能輸出1-9,后面會介紹原因,先看如下代碼:
public class ConditionTest { public static void main(String[] args) { final AtomicInteger value = new AtomicInteger(1); final Lock lock = new ReentrantLock(); final Condition reachThreeCondition = lock.newCondition(); final Condition reachSixCondition = lock.newCondition(); Thread threadA = new Thread(new Runnable() { public void run() { lock.lock(); System.out.輸出結果1:線程2拿到鎖【1】線程2【1】進入等待線程1拿到鎖【1】123線程1【1】調用喚醒【reachThreeCondition】線程1【1】釋放鎖線程1拿到鎖【2】線程1【2】進入等待線程2【1】被喚醒線程2【1】釋放了鎖線程2拿到鎖【2】456線程2【2】喚醒了【reachSixCondition】線程2【2】釋放了鎖線程1【2】被喚醒789輸出結果2:
線程2拿到鎖【1】線程2【1】進入等待線程1拿到鎖【1】123線程1【1】調用喚醒【reachThreeCondition】線程1【1】釋放鎖線程2【1】被喚醒線程2【1】釋放了鎖線程2拿到鎖【2】456線程2【2】喚醒了【reachSixCondition】線程2【2】釋放了鎖線程1拿到鎖【2】線程1【2】進入等待輸出結果3:
線程1拿到鎖【1】123線程1【1】調用喚醒【reachThreeCondition】線程1【1】釋放鎖線程1拿到鎖【2】線程1【2】進入等待線程2拿到鎖【1】線程2【1】釋放了鎖線程2拿到鎖【2】456線程2【2】喚醒了【reachSixCondition】線程2【2】釋放了鎖線程1【2】被喚醒789上面列出了三種輸出結果,結果1其實是一個很理想的運行結果,所有代碼都執行完了,其實讀者可以反復運行上面代碼,會發現可能出現好幾種結果,這里只列出三種典型的結果。 我們來根據結果做如下分析: 1. 線程1和線程2同時啟動,此時首次出現鎖競爭,上述結果1是線程2獲取到鎖(此處也可能是線程1獲取到鎖,見結果3),則線程1處于掛起狀態,等待獲取鎖,線程2獲取鎖后判斷滿足條件,則使用reachThreeCondition.await()讓線程進入掛起狀態,且此處會釋放鎖。 2. 由于上一步線程2釋放了鎖,所以此時線程1可獲取到鎖,于是線程1獲取到鎖,并繼續執行輸出123,之后調用reachThreeCondition.signal()喚醒線程2,此時線程1和線程2同時運行,線程1和2分別繼續執行后續釋放鎖的代碼(此時線程2其實沒有鎖可釋放)。 3. 此時又會出現鎖競爭,線程1和線程2會競爭鎖(結果1和結果3是由線程1拿到了鎖,結果2是由線程2拿到了鎖),此時分兩種情況: a.(1)線程1拿到鎖,之后滿足條件通過reachSixCondition掛起并釋放鎖,(2)此時線程2拿到鎖輸出456之后喚醒reachSixCondition,之后倆線程繼續各自執行后續代碼到程序結束,完整輸出1-9. b.線程2拿到鎖,之后不滿足條件釋放鎖,此時第三次出現鎖競爭,會出現兩種結果,1)線程1拿到鎖則執行a邏輯,完整輸出1-9;2)線程2拿到鎖,則執行線程2輸出4-6,之后釋放鎖,線程1獲取鎖,之后執行reachSixCondition掛起,由于此時線程2已執行結束,則線程1的reachSixCondition沒有地方被喚醒,所以線程1會一直處于掛起狀態。
所以通過上面跟蹤代碼,理解了Condition的使用,其實在于可以根據不同條件來掛起、喚醒線程,且會自動釋放鎖,以達到可以更加方便的實現線程的有序性。
歡迎關注個人博客:blog.scarlettbai.com
新聞熱點
疑難解答