你們都用過ReentrantLock,可是你們對內部實現是否足夠了解呢,下面我就簡單說一下其中的實現原理。node
ReentrantLock是可重入鎖,也就是同一個線程能夠屢次獲取鎖,每獲取一次就會進行一次計數,解鎖的時候就會遞減這個計數,直到計數變爲0。app
它有兩種實現,一種是公平鎖,一種是非公平鎖,那麼默認是什麼鎖呢?看完以下代碼想必你也知道了。 less
/** * Creates an instance of {@code ReentrantLock}. * This is equivalent to using {@code ReentrantLock(false)}. */ public ReentrantLock() { sync = new NonfairSync(); }
它的內部結構的實現是如何的呢? 首先NonFairSync類是靜態內部類,它繼承了Sync。ui
/** * Sync object for non-fair locks */ static final class NonfairSync extends Sync
Sync繼承了AbstractQueuedSynchronizer,簡稱AQS。同時Sync裏邊實現了tryRelease方法,由於公平鎖和非公平鎖均可以用這個方法釋放鎖。this
/** * Base of synchronization control for this lock. Subclassed * into fair and nonfair versions below. Uses AQS state to * represent the number of holds on the lock. */ abstract static class Sync extends AbstractQueuedSynchronizer
繼續看非公平鎖的lock方法,採用CAS進行當前狀態的設置state=0,表示沒有線程佔用,state=1表示已經有現成佔用了,設置成功了,將當前線程設置爲線程擁有者,而且是排他的。若是有現成佔用了,那麼須要進入acquire(1),須要獲取一個鎖。spa
/** * Performs lock. Try immediate barge, backing up to normal * acquire on failure. */ final void lock() { if (compareAndSetState(0, 1)) setExclusiveOwnerThread(Thread.currentThread()); else acquire(1); }
acquire方法首先進行tryAcquire,嘗試獲取鎖,即調用nonfairTryAcquire,判斷當前鎖是否state=0, 則沒有現成佔用,則進行設置。若是被佔用了判斷該線程是不是當前線程佔用的,若是是的話,那麼能夠進行重入,即當前能夠獲取鎖,計數器進行加1。不然的話返回失敗。返回失敗後執行addWaiter方法,也就是添加到等待的隊列。Node是一個雙向列表,也就是把須要等待的線程放到放到Node,而且連接起來。線程
/** * Acquires in exclusive mode, ignoring interrupts. Implemented * by invoking at least once {@link #tryAcquire}, * returning on success. Otherwise the thread is queued, possibly * repeatedly blocking and unblocking, invoking {@link * #tryAcquire} until success. This method can be used * to implement method {@link Lock#lock}. * * @param arg the acquire argument. This value is conveyed to * {@link #tryAcquire} but is otherwise uninterpreted and * can represent anything you like. */ public final void acquire(int arg) { if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); } /** * Performs non-fair tryLock. tryAcquire is implemented in * subclasses, but both need nonfair try for trylock method. */ 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; } /** * Creates and enqueues node for current thread and given mode. * * @param mode Node.EXCLUSIVE for exclusive, Node.SHARED for shared * @return the new node */ private Node addWaiter(Node mode) { Node node = new Node(Thread.currentThread(), mode); // Try the fast path of enq; backup to full enq on failure Node pred = tail; if (pred != null) { node.prev = pred; if (compareAndSetTail(pred, node)) { pred.next = node; return node; } } enq(node); return node; }
接下來看一下Node的大體內容,有兩個指針,一個是prev,一個是next,還保存着當前的線程。同時裏邊還有一個共享鎖和獨佔鎖,SHARED和EXCLUSIVE。ReentrantLock採用的就是獨佔鎖。Semaphore,CountDownLatch等採用的是共享鎖,即有多個線程能夠同時獲取鎖。指針
static final class Node { /** Marker to indicate a node is waiting in shared mode */ static final Node SHARED = new Node(); /** Marker to indicate a node is waiting in exclusive mode */ static final Node EXCLUSIVE = null; /** * Link to predecessor node that current node/thread relies on * for checking waitStatus. Assigned during enqueuing, and nulled * out (for sake of GC) only upon dequeuing. Also, upon * cancellation of a predecessor, we short-circuit while * finding a non-cancelled one, which will always exist * because the head node is never cancelled: A node becomes * head only as a result of successful acquire. A * cancelled thread never succeeds in acquiring, and a thread only * cancels itself, not any other node. */ volatile Node prev; /** * Link to the successor node that the current node/thread * unparks upon release. Assigned during enqueuing, adjusted * when bypassing cancelled predecessors, and nulled out (for * sake of GC) when dequeued. The enq operation does not * assign next field of a predecessor until after attachment, * so seeing a null next field does not necessarily mean that * node is at end of queue. However, if a next field appears * to be null, we can scan prev's from the tail to * double-check. The next field of cancelled nodes is set to * point to the node itself instead of null, to make life * easier for isOnSyncQueue. */ volatile Node next; /** * The thread that enqueued this node. Initialized on * construction and nulled out after use. */ volatile Thread thread; /** * Link to next node waiting on condition, or the special * value SHARED. Because condition queues are accessed only * when holding in exclusive mode, we just need a simple * linked queue to hold nodes while they are waiting on * conditions. They are then transferred to the queue to * re-acquire. And because conditions can only be exclusive, * we save a field by using special value to indicate shared * mode. */ Node nextWaiter;
大體的思路咱們看了一下,整體的流程圖我畫了一下。ReentrankLock內核採用的是AQS實現的,AQS裏邊採用的是雙向鏈表,即若是當前線程未獲取到鎖將會加入到鏈表中。code
那麼公平鎖和非公平鎖的實現的不一樣點在哪裏呢?公平鎖和非公平鎖就差在 !hasQueuedPredecessors() ,也就是前邊沒有排隊者的話,我就能夠獲取鎖了。orm
/** * Fair version of tryAcquire. Don't grant access unless * recursive call or no waiters or is first. */ protected final boolean tryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { if (!hasQueuedPredecessors() && compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current); return true; } } else if (current == getExclusiveOwnerThread()) { int nextc = c + acquires; if (nextc < 0) throw new Error("Maximum lock count exceeded"); setState(nextc); return true; } return false; }
若是當前線程以前還有線程等待就會返回true,若是當前節點是頭結點,或者當前隊列爲空就會返回false。非公平鎖沒有這句話的判斷,因此直接去競爭鎖。
/** * Queries whether any threads have been waiting to acquire longer * than the current thread. * * <p>An invocation of this method is equivalent to (but may be * more efficient than): * <pre> {@code * getFirstQueuedThread() != Thread.currentThread() && * hasQueuedThreads()}</pre> * * <p>Note that because cancellations due to interrupts and * timeouts may occur at any time, a {@code true} return does not * guarantee that some other thread will acquire before the current * thread. Likewise, it is possible for another thread to win a * race to enqueue after this method has returned {@code false}, * due to the queue being empty. * * <p>This method is designed to be used by a fair synchronizer to * avoid <a href="AbstractQueuedSynchronizer#barging">barging</a>. * Such a synchronizer's {@link #tryAcquire} method should return * {@code false}, and its {@link #tryAcquireShared} method should * return a negative value, if this method returns {@code true} * (unless this is a reentrant acquire). For example, the {@code * tryAcquire} method for a fair, reentrant, exclusive mode * synchronizer might look like this: * * <pre> {@code * protected boolean tryAcquire(int arg) { * if (isHeldExclusively()) { * // A reentrant acquire; increment hold count * return true; * } else if (hasQueuedPredecessors()) { * return false; * } else { * // try to acquire normally * } * }}</pre> * * @return {@code true} if there is a queued thread preceding the * current thread, and {@code false} if the current thread * is at the head of the queue or the queue is empty * @since 1.7 */ public final boolean hasQueuedPredecessors() { // The correctness of this depends on head being initialized // before tail and on head.next being accurate if the current // thread is first in queue. Node t = tail; // Read fields in reverse initialization order Node h = head; Node s; return h != t && ((s = h.next) == null || s.thread != Thread.currentThread()); }
這個就是ReentrantLock的基本原理,接下來我們繼續看看與之一塊使用的Condition。Condition是一個接口,它的實現類是ConditionObject。調用await的時候也會將當前線程的一些信息加入到隊列當中。ConditionObject中有一個firstWaiter和LastWaiter分別指向的了等待隊列的頭和尾。
當調用Condition的signal方法是,則會將第一個Node轉換到同步隊列,以下圖所示。
好了,總結一下:
1. ReentrankLock默認是非公平鎖。
2.ReentrankLock的內部實現採用的AQS的雙向鏈表實現。獲取鎖的線程會被封裝成Node裏邊,供後續使用。
3.公平鎖採用判斷當前Node是否是頭結點,若是是的話就獲取鎖並作業務處理,不是頭結點的不能獲取所。
4.非公平鎖沒有判斷當前結點,採用CAS,誰第一個拿到了state=0,則視爲獲取鎖。
5.Condition的await和notify也採用相似的機制,當執行await是,會將當前線程信息的相關信息放入到Node的列表,記錄firstWaiter和lastWaiter指向的信息。
但願對你們有所幫助,若是有問題的請及時指出。