ReentrantLock實現原理


ReentrantLock主要利用CAS+CLH隊列來實現。它支持公平鎖和非公平鎖,兩者的實現類似。

  • CAS:Compare and Swap,比較並交換。CAS有3個操作數:內存值V、預期值A、要修改的新值B。當且僅當預期值A和內存值V相同時,將內存值V修改為B,否則什么都不做。該操作是一個原子操作,被廣泛的應用在Java的底層實現中。在Java中,CAS主要是由sun.misc.Unsafe這個類通過JNI調用CPU底層指令實現。
  • CLH隊列:帶頭結點的雙向非循環鏈表(如下圖所示):

CLH隊列

ReentrantLock的基本實現可以概括為:先通過CAS嘗試獲取鎖。如果此時已經有線程占據了鎖,那就加入CLH隊列並且被掛起。當鎖被釋放之后,排在CLH隊列隊首的線程會被喚醒,然后CAS再次嘗試獲取鎖。在這個時候,如果:

  • 非公平鎖:如果同時還有另一個線程進來嘗試獲取,那么有可能會讓這個線程搶先獲取;
  • 公平鎖:如果同時還有另一個線程進來嘗試獲取,當它發現自己不是在隊首的話,就會排到隊尾,由隊首的線程獲取到鎖。

下面分析下兩個片段:

final boolean nonfairTryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { if (compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current); return true; } } else if (current == getExclusiveOwnerThread()) { int nextc = c + acquires; if (nextc < 0) // overflow throw new Error("Maximum lock count exceeded"); setState(nextc); return true; } return false; } 

在嘗試獲取鎖的時候,會先調用上面的方法。如果狀態為0,則表明此時無人占有鎖。此時嘗試進行set,一旦成功,則成功占有鎖。如果狀態不為0,再判斷是否是當前線程獲取到鎖。如果是的話,將狀態+1,因為此時就是當前線程,所以不用CAS。這也就是可重入鎖的實現原理。

final boolean acquireQueued(final Node node, int arg) { boolean failed = true; try { boolean interrupted = false; for (;;) { final Node p = node.predecessor(); if (p == head && tryAcquire(arg)) { setHead(node); p.next = null; // help GC failed = false; return interrupted; } if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) interrupted = true; } } finally { if (failed) cancelAcquire(node); } } private final boolean parkAndCheckInterrupt() { LockSupport.park(this); return Thread.interrupted(); } 

該方法是在嘗試獲取鎖失敗加入CHL隊尾之后,如果發現前序節點是head,則CAS再嘗試獲取一次。否則,則會根據前序節點的狀態判斷是否需要阻塞。如果需要阻塞,則調用LockSupport的park方法阻塞該線程。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM