標籤:head ima als 問題 ola 編譯報錯 height 一個 mil
圖解Condition0. demo
我先給出一個demo, 這樣大家就可以根據我給的這段代碼, 邊調試邊看源碼了. 還是那句話: 注意"My" , 我把ReentrantLock類 改名為了 "MyReentrantLock"類 , "Lock"類 改名為了"MyLock"類. 大家粘貼My Code的時候, 把相應的"My"都去掉就好了, 否則會編譯報錯哦.
import java.util.Scanner;import java.util.concurrent.locks.Condition;import java.util.function.Supplier;public class ConditionTest { static final Scanner scanner = new Scanner(System.in); static volatile String cmd = ""; private static MyReentrantLock lock = new MyReentrantLock(true); private static Condition condition = lock.newCondition(); public static void main(String[] args) { for (String name : new String[]{"w1", "w2", "w3", "w4", "w5", "w6"}) new Thread(() -> func(() -> lock, name)).start(); new Thread(() -> signalOne(() -> lock, "s")).start(); while (scanner.hasNext()) { cmd = scanner.nextLine(); } } public static void func(Supplier<MyLock> myLockSupplier, String name) { blockUntilEquals(() -> cmd, name); myLockSupplier.get().lock(); System.out.println(name + "阻塞等待..."); try { condition.await(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("釋放了" + name); myLockSupplier.get().unlock(); } public static void signalOne(Supplier<MyLock> myLockSupplier, String name) { while (true) { blockUntilEquals(() -> cmd, name); myLockSupplier.get().lock(); condition.signal(); System.out.println("通知喚醒了一個等待..."); myLockSupplier.get().unlock(); } } private static void blockUntilEquals(Supplier<String> cmdSupplier, final String expect) { while (!cmdSupplier.get().equals(expect)) quietSleep(1000); clearCmd(); } private static void quietSleep(int mills) { try { Thread.sleep(mills); } catch (InterruptedException e) { e.printStackTrace(); } } private static void clearCmd() { cmd = ""; }}
使用例子在下面.
首先輸入w1, 讓線程1執行await() . 然後輸入w2, 讓線程2執行await(). 然後輸入w3, 讓線程3執行await().
接下來輸入3次 s, 沒輸入一次s, 並按下斷行符號, 就會signal通知一個await等待.
1. 開始圖解Condition
想用ReentrantLock的Condition, 那麼就首先要有個ReentrantLock鎖.
執行個體化一個鎖, ReentrantLock裡只有一個成員變數sync.
sync執行個體裡面有四個成員變數.
分別表示:
1. state - 鎖計數器
2. exclusiveOwnerThread - 鎖的持有線程
3. head - `等待隊列`的頭結點.
4. tail - 指向`等待隊列`的最後一個元素
然後咱們執行個體化了一個Condition.
當咱們輸入w1後, 第一個線程就申請了鎖, 並且申請成功.
然後就執行到了await()方法.
將線程1封裝為Node節點, 然後waitState置為-2. -2的含義是Condition.
await()方法內部的第一個步驟就是把當前線程(線程1)插入到了`條件隊列`中.
然後就開始釋放當前線程(線程1)的鎖了, 而且是完全釋放, 一次就釋放掉全部重入次數哦, 也就是直接讓state等於0.
釋放完鎖了, 然後掛起線程1.
然後讓線程2進行await.( 也就是前面的demo程式中在控制台輸入了w2.)
線程2執行await()之前當然是先擷取鎖了.
由於此時, 鎖是閒置. 所以線程2成功擷取到了鎖. 淡橙色的陰影部分為變化的內容:
擷取鎖之後, 線程2就該執行await()了:
如, 將線程封裝為Node, 然後尾插到`條件隊裡`中, 只是await() 方法的第一步.
然後的操作, 就是完全釋放線程2的鎖, 然後掛起線程.
如果這個時候咱們在上面demo程式的控制台輸入"s", 那麼就會讓線程s 申請鎖, 申請成功後, 就會執行signal.
首先是線程s申請鎖成功:
線程s成功擷取了鎖以後, 就是該執行signal()了.
首先將`條件隊列`裡的第一個節點脫離出來:
然後把waitState從-2改為0 :
隨後要做的就是把從`條件隊列`中脫離出來的Node(就是線程1對應的Node節點), 尾插到`等待隊列`中.
但是`等待隊列`此時還未被初始化, 所以插入到`等待隊列`之前, 要把`等待隊列`初始化了. 見:
`等待隊列`初始化完了. 接下來就是把線程1對應的Node, 尾插到`等待隊列`中了:
然後將當前尾插的那個節點的前驅的waitState置為-1. -1表示下一個節點等待著被喚醒.
接下來就是線程s會執行到unlock(). 然後就會釋放鎖, 之後就是喚醒`等待隊列`中的第一個線程.
如此就介紹完了signal()
一個signal命令, 就把一個await的線程從`條件隊列中`移到了`等待隊列`中. 到了等待隊列中之後, 剩下的就是跟"鎖解鎖後, 喚醒下一個執行"這樣的步驟一樣了.
我覺得await方法就是將線程尾插到`條件隊列`中. signal()方法就是把條件隊列中的第一個元素, 尾插入到`等待隊列`中.
所以我覺得不必往下分析了.
也可能是我理所當然了, 有問題的話之後再補充.
[圖解Java]Condition