1、AQS-條件變量的支持(二)
-
在以下代碼中,當另一個線程調用條件變量的signal方法的時候(必須先調用鎖的lock方法獲取鎖),在內部會把條件隊列裏面隊頭的一個線程節點從條件隊列裏面移除而且放入AQS的阻塞隊列裏面,而後激活這個線程。
public final void signal() {
if(!isHeldExclusively()) {
throw IllegalMonitorException();
}
Node first = firstWaiter;
if(first != null){
doSignal(first);
}
}
-
須要注意的是,AQS提供了ConditionObject的實現,並無提供newCondition函數,該函數用來new一個ConditionObject對象,須要由AQS的子類來提供newConditon函數
-
下面來看當一個線程調用條件變量的await()方法而被阻塞後,如何將其放入條件隊列
private Node addConditionWaiter() {
Node t = lastWaiter;
...
Node node = new Node(Thread.currentThread(),Node.CONDITION);
if(t == null){
firstWaiter = node;
}else {
t.nextWaiter = node;
}
lastWaiter = node;
return node;
}
-
代碼(1)首先根據根據當前線程建立了一個類型爲Node.CONDITION的節點,而後經過代碼(2),(3),(4)在單向隊列尾部插入一個元素
-
注意:當多個線程同時調用lock.lock()方法獲取鎖時,只有一個線程獲取到了鎖,其餘線程會被轉換爲Node節點插入到lock鎖對應的AQS阻塞裏面,而且作自旋CAS嘗試獲取鎖
-
若是獲取到了鎖的線程又調用對應條件變量的await()方法,則該線程會釋放獲取到的鎖,並被轉化爲Node節點插入到條件變量對應的條件隊列裏面
-
這時候由於調用lock.lock()方法被阻塞到AQS隊列裏面的一個線程會獲取到被釋放的鎖,若是該線程也調用了條件變量的await()方法則該線程也會被放入條件變量的條件隊列裏面
-
當另一個線程調用條件變量的signal()或者signalAll()方法的時候,會把條件隊列裏面的一個或者所有Node節點移動到AQS的阻塞隊列裏面,等待時機獲取鎖。
-
最後使用一個圖總結:一個鎖對應一個AQS阻塞隊列,對應多個條件變量,每一個條件變量有本身的一個條件隊列。
-
23.1
2、基於AQS實現自定義同步器
-
基於AQS實現一個不可重入的鎖,自定義AQS須要重寫一系列的函數,還須要定義原子變量state的含義,在這裏咱們定義state爲0表示目前鎖沒有被線程持有,state爲1表示所已經被某一個線程持有,因爲是不可重入鎖,因此不須要記錄持有鎖的線程獲取鎖的次數,另外,咱們自定義的鎖支持條件變量。
-
package com.ruigege.LockSourceAnalysis6;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
public class NonReentrantLockME implements Lock,java.io.Serializable{
private static class Sync extends AbstractQueueSynchronizer {
protected boolean isHeldExclusively() {
return getState() == 1;
}
public boolean tryAcquire(int acquires) {
assert acquires == 1;
if(compareAndSetState(0,1)) {
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
protected boolean tryRelease(int release) {
assert releases == 1;
if(getState() == 0) {
throw new IllegalMonitorStateException();
}
setExclusiveOwnerThread(null);
setState(0);
return true;
}
Condition newConditon() {
return new ConditionObject();
}
}
private final Sync sync = new Sync();
public void lock() {
sync.acquire(1);
}
public boolean tryLock() {
return sync.tryAcquire(1);
}
public void unlock() {
sync.release(1);
}
public Condition newCondition() {
return sync.newConditon();
}
public boolean isLocked() {
return sync.isHeldExclusively();
}
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
public boolean tryLock(long timeout,TimeUnit unit) throws InterruptedException {
return sync.tryAcquireNanos(1,unit.toNanos(timeout));
}
}
-
如上面的代碼,NonReentrantLock定義了一個內部類Sync用來實現具體的鎖的操做,Sync則繼承了AQS ,因爲咱們實現的獨佔模式的鎖,因此Sync重寫了tryAcquire\tryRelease和isHeldExclusively3個方法,另外Sync提供了newCondition這個方法用來支持條件變量。
3、源碼:
-
所在包:com.ruigege.ConcurrentListSouceCodeAnalysis5
-
https://github.com/ruigege66/ConcurrentJava
-
-
-
歡迎關注微信公衆號:傅里葉變換,我的帳號,僅用於技術交流