上文學習了JUC的AQS工具,下面看下可重入顯示鎖的實現(如何基於AQS),要看ReentrantLock,還是先看下顯示鎖內部對AQS的幾種子類實現。
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = -5179523762034025860L;
/**
* Performs {@link Lock#lock}. The main reason for subclassing
* is to allow fast path for nonfair version.
*/
abstract void lock();//lock留給子類實現
/**
* 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();//AQS的state屬性,這裏被用來當作鎖資源
if (c == 0) {//state在AQS中默認爲0,說明此時到達的是第一個線程,尚未有線程修改過state,acquires是要獲取的資源數,在互斥鎖下爲1
if (compareAndSetState(0, acquires)) {//CAS修改state爲acquires
setExclusiveOwnerThread(current);//將當前線程賦值給AQS的exclusiveOwnerThread屬性
return true;
}
}
else if (current == getExclusiveOwnerThread()) {//state!=0說明已有線程獲取了資源,檢查AQS的互斥線程是否是線程自身
int nextc = c + acquires;//將請求資源數增加
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);//更改state(未使用cas,因爲只有exclusiveOwnerThread可以修改此值)
return true;
}
return false;
}
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())//只有exclusiveOwnerThread當前擁有着纔可以release釋放
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {//state==0,則當前exclusiveOwnerThread線程已釋放所有資源
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
protected final boolean isHeldExclusively() {
// While we must in general read state before owner,
// we don't need to do so to check if current thread is owner
return getExclusiveOwnerThread() == Thread.currentThread();
}
final ConditionObject newCondition() {//條件對象,擁有await和signal等方法,用於條件上的線程阻塞和喚醒
return new ConditionObject();
}
// Methods relayed from outer class
final Thread getOwner() {
return getState() == 0 ? null : getExclusiveOwnerThread();
}
final int getHoldCount() {
return isHeldExclusively() ? getState() : 0;
}
final boolean isLocked() {
return getState() != 0;
}
/**
* Reconstitutes the instance from a stream (that is, deserializes it).
*/
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
s.defaultReadObject();
setState(0); // reset to unlocked state
}
}
非公平Sync和公平Sync實現: static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
/**
* Performs lock. Try immediate barge, backing up to normal
* acquire on failure.
*/
final void lock() {
if (compareAndSetState(0, 1))//cas(0,1)返回true則獲取成功將此線程設置爲exclusiveOwnerThread線程
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);//調用AQS的acquires方法
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);//調用Sync的方法
}
}
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
final void lock() {//公平獲取方法,對比非公平的lock
acquire(1);
}
/**
* 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;
}
}
對於AQS的使用,我們直接從ReentrantLock的lock和unlock方法看起就好
public void lock() {
sync.lock();
}
public void unlock() {
sync.release(1);
}
其實內部的源碼,還是我們前面看到的,AQS的內容以及公平和非公平兩種子類的實現,ReentrantLock的實現就是調用這些方法。下面上一張我畫的圖。
線程ABC獲取lock,隊列的變過程等,以及釋放鎖的過程。
其中BC都調用了park阻塞自身,然後添加到隊尾,當前線程釋放鎖時,清空exclusiveOwnerThread,並喚醒head的後繼,調用unpark。
另外,當A線程運行結束,釋放鎖之後,喚醒了B節點,這裏只是進行了喚醒,卻沒有任何將head出隊的操作,這讓我很疑惑,當B線程被喚醒後,去競爭鎖失敗,豈不是又會將一個包含了B線程的新節點添加到隊尾?並且head節點的後繼仍然是剛纔的B節點?
當B節點線程B被喚醒後,仍然還在循環內,此時直接在acquireQueued內的循環內執行,並不會新增加節點,而當此線程獲取成功時,會將head出隊,B作爲頭節點。
這裏要感謝高級交流羣的廣州-浪子,幫我解答了我的問題:
獨佔模式下,假設現在有 A B C三個線程。
A線程先進來,成功獲取鎖, tryAcuire = true = !true = false. 此時A線程不需要進入等待隊列
B線程進來,tryAcquire= false= !false= true.進入acquireQueued(addWaiter(Node.EXCLUSIVE), arg)
跳過入隊
如果B線程被喚醒後,且能成功tryAcuire的話,那麼就成功出隊。tryAcuire失敗的話再次阻塞(爲什麼會失敗?可能他這次被喚醒的資源給別的線程搶去了)