一、入題
ReentrantLock是Java並發包中互斥鎖,它有公平鎖和非公平鎖兩種實現方式,以lock()為例,其使用方式為:
ReentrantLock takeLock = new ReentrantLock(); // 獲取鎖 takeLock.lock(); try { // 業務邏輯 } finally { // 釋放鎖 takeLock.unlock(); }
那么,ReentrantLock內部是如何實現鎖的呢?接下來我們就以JDK1.7中的ReentrantLock的lock()為例詳細研究下。
二、ReentrantLock類的結構
ReentrantLock類實現了Lock和java.io.Serializable接口,其內部有一個實現鎖功能的關鍵成員變量Sync類型的sync,定義如下:
/** Synchronizer providing all implementation mechanics */ private final Sync sync;
而這個Sync是繼承了AbstractQueuedSynchronizer的內部抽象類,主要由它負責實現鎖的功能。關於AbstractQueuedSynchronizer我們會在以后詳細介紹,你只要知道它內部存在一個獲取鎖的等待隊列及其互斥鎖狀態下的int狀態位(0當前沒有線程持有該鎖、n存在某線程重入鎖n次)即可,該狀態位也可用於其它諸如共享鎖、信號量等功能。
Sync在ReentrantLock中有兩種實現類:NonfairSync、FairSync,正好對應了ReentrantLock的非公平鎖、公平鎖兩大類型。
三、獲取鎖主體流程
ReentrantLock的鎖功能主要是通過繼承了AbstractQueuedSynchronizer的內部類Sync來實現的,其lock()獲取鎖的主要流程如下:
首先,ReentrantLock的lock()方法會調用其內部成員變量sync的lock()方法;
其次,sync的非公平鎖NonfairSync或公平鎖FairSync實現了父類AbstractQueuedSynchronizer的lock()方法,其會調用acquire()方法;
然后,acquire()方法則在sync父類AbstractQueuedSynchronizer中實現,它只有一段代碼:
通過tryAcquire()方法試圖獲取鎖,獲取到直接返回結果,否則通過嵌套調用acquireQueued()、addWaiter()方法將請求獲取鎖的線程加入等待隊列,如果成功的話,將當前請求線程阻塞,and,over!
隊列如何實現及如何添加到隊列中以后再做詳細分析!這里只關注ReentrantLock的實現邏輯。
上述就是公平鎖、非公平鎖實現獲取鎖的主要流程,而針對每種鎖來說,其實現方式有很大差別,主要就體現在各自實現類的lock()和tryAcquire()方法中。在sync的抽象類Sync及其抽象父類AbstractQueuedSynchronizer中,lock()方法和tryAcquire()方法被定義為抽象方法或者未實現,而是由具體子類去實現:
/** * Performs {@link Lock#lock}. The main reason for subclassing * is to allow fast path for nonfair version. */ abstract void lock();
/** * Attempts to acquire in exclusive mode. This method should query * if the state of the object permits it to be acquired in the * exclusive mode, and if so to acquire it. * * <p>This method is always invoked by the thread performing * acquire. If this method reports failure, the acquire method * may queue the thread, if it is not already queued, until it is * signalled by a release from some other thread. This can be used * to implement method {@link Lock#tryLock()}. * * <p>The default * implementation throws {@link UnsupportedOperationException}. * * @param arg the acquire argument. This value is always the one * passed to an acquire method, or is the value saved on entry * to a condition wait. The value is otherwise uninterpreted * and can represent anything you like. * @return {@code true} if successful. Upon success, this object has * been acquired. * @throws IllegalMonitorStateException if acquiring would place this * synchronizer in an illegal state. This exception must be * thrown in a consistent fashion for synchronization to work * correctly. * @throws UnsupportedOperationException if exclusive mode is not supported */ protected boolean tryAcquire(int arg) { throw new UnsupportedOperationException(); }
下面,我們分別研究下非公平鎖和公平鎖的實現。
四、非公平鎖NonfairSync
1、lock()方法
通過代碼可以看到,非公平鎖上來就無視等待隊列的存在而搶占鎖,通過基於CAS操作的compareAndSetState(0, 1)方法,試圖修改當前鎖的狀態,這個0表示AbstractQueuedSynchronizer內部的一種狀態,針對互斥鎖則是尚未有線程持有該鎖,而>=1則表示存在線程持有該鎖,並重入對應次數,這個上來就CAS的操作也是非公共鎖的一種體現,CAS操作成功的話,則將當前線程設置為該鎖的唯一擁有者。
搶占不成功的話,則調用父類的acquire()方法,按照上面講的,繼而會調用tryAcquire()方法,這個方法也是由最終實現類NonfairSync實現的,如下:
protected final boolean tryAcquire(int acquires) { return nonfairTryAcquire(acquires); }
2、tryAcquire()
而這個nonfairTryAcquire()方法實現如下:
/** * 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; }
還是上來先判斷鎖的狀態,通過CAS來搶占,搶占成功,直接返回true,如果鎖的持有者線程為當前線程的話,則通過累加狀態標識重入次數。搶占不成功,或者鎖的本身持有者不是當前線程,則返回false,繼而后續通過進入等待隊列的方式排隊獲取鎖。可以通過以下簡單的圖來理解:
五、公平鎖FairSync
1、lock()
公平鎖的lock()方法就比較簡單了,直接調用acquire()方法,如下:
2、tryAcquire()
公平鎖的tryAcquire()方法也相對較簡單,如下:
/** * 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; }
當前線程會在得到當前鎖狀態為0,即沒有線程持有該鎖,並且通過!hasQueuedPredecessors()判斷當前等待隊列沒有前繼線程(也就是說,沒有比我優先級更高的線程在請求鎖了)獲取鎖的情況下,通過CAS搶占鎖,並設置自己為鎖的當前擁有者,當然,如果是重入的話,和非公平鎖處理一樣,通過累加狀態位標記重入次數。
而一旦等待隊列中有等待者,或當前線程搶占鎖失敗,則它會乖乖的進入等待隊列排隊等待。公平鎖的實現大致如下:
六、默認實現
ReentrantLock的默認實現為非公平鎖,如下:
當然,你也可以通過另外一個構造方法指定鎖的實現方式,如下:
七、其它
即便是公平鎖,如果通過不帶超時時間限制的tryLock()的方式獲取鎖的話,它也是不公平的,因為其內部調用的是sync.nonfairTryAcquire()方法,無論搶到與否,都會同步返回。如下:
public boolean tryLock() { return sync.nonfairTryAcquire(1); }
但是帶有超時時間限制的tryLock(long timeout, TimeUnit unit)方法則不一樣,還是會遵循公平或非公平的原則的,如下:
public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException { return sync.tryAcquireNanos(1, unit.toNanos(timeout)); }
其它流程都比較簡單,讀者可自行翻閱Java源碼查看!