我先給出一個demo, 這樣你們就能夠根據我給的這段代碼, 邊調試邊看源碼了. 仍是那句話: 注意"My" , 我把ReentrantLock類 更名爲了 "MyReentrantLock"類 , "Lock"類 更名爲了"MyLock"類. 你們粘貼個人代碼的時候, 把相應的"My"都去掉就行了, 不然會編譯報錯哦.java
import java.util.Scanner; import java.util.function.Supplier; public class Main { static final Scanner scanner = new Scanner(System.in); static volatile String cmd = ""; private static MyReentrantLock lock = new MyReentrantLock(true); public static void main(String[] args) { for (String name : new String[]{"1", "2", "3", "4", "5", "6"}) new Thread(() -> func(() -> lock, name)).start(); while (scanner.hasNext()) { cmd = scanner.nextLine(); } } public static void func(Supplier<MyLock> myLockSupplier, String name) { blockUntilEquals(() -> cmd, "lock " + name); myLockSupplier.get().lock(); System.out.println("獲取了" + name + "號鎖"); blockUntilEquals(() -> cmd, "unlock " + name); myLockSupplier.get().unlock(); System.out.println("釋放了" + name + "號鎖"); } private static void blockUntilEquals(Supplier<String> cmdSupplier, final String expect) { while (!cmdSupplier.get().equals(expect)) quietSleep(1000); } private static void quietSleep(int mills) { try { Thread.sleep(mills); } catch (InterruptedException e) { e.printStackTrace(); } } }
使用例子在下面. 首先線程1申請了鎖, 成功申請. 而後線程2申請了鎖, 未申請到, 進入等待隊列中. 線程3 和 線程4 也申請失敗, 進入到等待隊列中. ui
隨後釋放了鎖1, 而後鎖2就獲取到鎖了. 而後釋放了鎖2, 鎖3就獲取到鎖了...而後是鎖4. 大概就是這個使用. 用個人這段代碼配合着debug, 能夠很清楚地調試出代碼的執行流程.線程
一個ReentrantLock()實例裏只有一個sync成員變量.debug
假設我們建立了一個公平鎖, 那麼sync是FairSync類的實例.3d
sync實例裏面有四個成員變量.指針
分別表示:調試
1. state - 鎖計數器blog
2. exclusiveOwnerThread - 鎖的持有線程隊列
3. head - `等待隊列`的頭結點.get
4. tail - 指向`等待隊列`的最後一個元素
如今鎖是空閒狀態.
當線程1申請了鎖, 會把state置爲1. 而後把鎖的exclusiveOwnerThread指向本身(線程1). 這就算是持有鎖了.其餘線程沒法再獲取鎖了.只能等線程1釋放.
若是線程1在此對這個鎖執行了lock()方法呢?
那麼就是鎖的重入了, 也就是說這個線程再次進入(獲取)了這個鎖 會讓state+1.
再重入呢? 那就再加1....
能夠重入多少次呢? 能夠重入, 直到整形int溢出爲止...
接下來, 線程1還沒釋放鎖呢, 線程2就想獲取鎖了. 那麼會發生什麼呢:
把線程2封裝爲一個Node類型的節點. 而後打算把這個Node放到`等待隊列`中去.
這個時候`等待隊列`纔會被創建, 由於這個時候才須要`等待隊列`, 這種叫懶初始化.
這個時候, `等待隊列`的頭結點產生了. 而後把`等待隊列`的tail也指向head.
head或者tail 不爲null, 表示`等待隊列`被創立了.
head==tail 表示, `等待隊列`爲空, 裏面沒有`有效元素`.
`等待隊列`有了. 線程2對應的Node也有了. 就差把這個Node插入到隊尾了.
首先讓tail指向線程2對應的Node.
而後分別維護兩個Node的前驅和後繼.(看下面紫色箭頭)
已經將線程2對應的Node插入到`等待隊列`的尾部了, 接下來讓線程1對應的Node裏的waitState等於-1
以後線程2就能夠安心的掛起了. 等線程1徹底釋放鎖的時候, 就會喚醒線程2了.
爲何說是`徹底釋放`呢? 由於鎖的的state如今等於3. 須要線程1 unlock()釋放3次鎖, 纔算是徹底釋放.
接下來, 線程1還沒釋放鎖呢, (線程2也沒輪到鎖呢). 線程3就想獲取鎖了. 那麼會發生什麼呢:
首先會建立一個線程3對應的Node節點.
而後讓尾指針tail指向這個最新的Node.
而後維護前驅和後繼(紫色箭頭), 來維持雙向鏈表.
接下來就會讓新節點的前驅節點的waitStatus = -1.
-1表示, 有下一個節點等待被喚醒.
而後線程3就能夠安心的掛起了.
等線程2 搶到鎖, 用完了釋放後, 就會去喚醒線程3.
我們讓線程1 unlock() 一次.
state減1了.
此時, 鎖並無釋放, 仍是被線程1持有.
我們再讓線程1 unlock() 一次.
state減1了. 但仍然大於0.
此時, 鎖並無釋放, 仍是被線程1持有.
我們再讓線程1 unlock() 一次.
state減1了. 這回state等於0了. 表示徹底釋放了鎖.
exclusiveOwnerThread也置爲了null, 表示當前的鎖不被任何線程持有.
準備喚醒下一個, 也就是`等待隊列`的第一個元素(線程2)
線程2被喚醒
而後鎖的state被置爲了1.
鎖的exclusiveOwnerThread指向了線程2. 表示當前鎖被線程2持有了.
既然線程1已經徹底釋放鎖了. 那麼就換線程2來當`等待隊列`的頭結點.
因此此時, 頭結點的含義就是: 當前持有鎖的線程對應的Node結點.
而後斷開相應的前驅和後繼, 讓線程1對應的Node徹底脫離`等待隊列` .
到此, 線程1釋放後, 線程2 獲取鎖的步驟就都執行完了.
接下來, 我們讓線程2釋放鎖.
state減1後等於0了.
因而鎖就徹底釋放了. exclusiveOwnerThread就被置爲null了.
而後是waitStatus被改回了0. 線程2對應的Node立刻就要離開`等待隊列`了
線程3被喚醒.
讓state=1, 並把鎖的exclusiveOwnerThread指向本身. 表示線程3本身獨佔了這把鎖.
修改head指針, 並斷開相應的前驅和後繼連接, 讓線程2對應的Node完全離開`等待隊列`
最後, 我們讓線程3釋放鎖.
state歸零.
exclusiveOwnerThread清空.
鎖空閒.
而head和tail仍然指向原先的Node. 之後等待隊列的頭結點就不須要從新初始化了.