一、ReetrantLock
1、 公平鎖,ReentrantLock reentrantLock = new ReentrantLock(true); reentrantLock.lock();設置鎖爲公平鎖。
1-1、 內部獲取定義的變量state,判斷是否爲0,如果爲0,則代表鎖已經被佔用
1-2、判斷是否需要排隊:
① 判斷隊首,隊尾是否相同,隊列未初始化時,隊列爲空,隊首隊尾相同,返回fasle。
② 隊列已經初始化,但是隊列中只有一個空的node節點,沒有排隊的線程,隊首隊尾相同,返回fasle。
③ 隊列已經初始化,隊首空node下有一個新的node,並且是這個node中的線程獲取鎖,返回false。
這裏如果返回false,!hasQueuedPredecessors返回true,則代表可以獲取鎖
1-3 調用unsafe方法,修改state的值爲1。
1-4、設置持有鎖的線程爲當前線程,加鎖成功,返回true。!tryAcquire(arg) 結果爲false,執行 selfInterrupt();回覆線程實際的執行狀態。
2、 如果1 中的條件不成立,則標識獲取鎖失敗,需要進行排隊嘗試。
2-1 addWaiter(Node.EXCLUSIVE), arg),嘗試初始化排隊隊列。
① 如果隊列已經初始化,隊尾增加一個node,維護一個雙向鏈表。
② 如果隊列未初始化,則需要初始化一個隊列,這裏是一個死循環,初始化完成跳出,判斷隊尾爲null,則在隊首初始化一個線程爲空的node(表示隊列中可能正在運行的線程,或者是持有鎖的線程),把,新的node追加到初始化的node之後。
維護一個如下圖一樣的隊列,隊首的node的thread一直爲null。
3 獲取隊列
3-1 查看一下當前node的前一個是否是隊首,如果不是則 證明前面已經有人在排隊了,直接park
3-2 如果前一個是隊首,則在此嘗試獲取鎖,如果能 獲取,則返回表示加鎖成功。
3-3 如果都沒有成功,則修改前一個waitStatus,爲-1,如果大於0,表示前一個node已經被cancled,則移除前一個node,直到找到一個可用的node。並且LockSupport.park(this);線程讓出cpu,等待喚醒。
二、解鎖過程:reentrantLock.unlock();
1、嘗試解鎖,比較簡單,對state -1,如果值爲0,則代表可以釋放,清空當前運行線程,並返回true
2、unparkSuccessor(h)釋放下一個線程
if (ws < 0) compareAndSetWaitStatus(node, ws, 0); 這個是將頭節點設置狀態設置爲0,
① 下次節點進纔會把頭改爲-1,才證明有新的線程加入,否則一直爲0,
② 下面代碼判斷的時候如果只有一個頭節點,node.next也爲null,頭尾一個節點,t!= null,但是頭的節點的thread爲null,如果①步驟不改,調用unpark會報錯。