ArrayList源碼分析--jdk1.8
LinkedList源碼分析--jdk1.8
HashMap源碼分析--jdk1.8
AQS源碼分析--jdk1.8
ReentrantLock源碼分析--jdk1.8
??1. AQS是一個基于FIFO隊列,可以用于構建鎖或者其他相關同步裝置的基礎框架。
??2. AQS提供了雙向鏈表。
??3. AQS分為共享模式和獨占模式。
??4.AQS基于volatile內存可見性和CAS原子性操作實現線程間通信操作。
??數據結構是集合的精華所在,數據結構往往也限制了集合的作用和側重點,了解各種數據結構是我們分析源碼的必經之路。
??AQS的數據結構如下:雙向鏈表
??
??AQS實現共享資源的訪問控制基礎:
?? ??1.state字段,即同步器狀態字段。用于共享資源的訪問控制
?? ??2.CLH隊列,FIFO等待隊列,存放競爭失敗的線程。通常CLH隊列是一個自旋隊列,AQS以阻塞的方式實現
?? ??CLH隊列的使用:
自旋鎖
學習了解自旋鎖之前先回顧一下互斥鎖
互斥鎖
線程在獲取互斥鎖的時候,如果發現鎖已經被其它線程占有,那么線程就會驚醒休眠,然后在適當的時機(比如喚醒)在獲取鎖。
自旋鎖
那么自旋鎖顧名思義就是“自旋”。就是當一個線程在嘗試獲取鎖失敗之后,線程不會休眠或者掛起,而是一直在循環檢測鎖是否被其它線程釋放。
區別
互斥鎖就是開始開銷要大于自旋鎖。臨界區持鎖時間的大小并不會對互斥鎖的開銷造成影響,而自旋鎖是死循環檢測,加鎖全程消耗cpu,起始開銷雖然低于互斥鎖,但是隨著持鎖時間,加鎖的開銷是線性增長。
適用的情況
互斥鎖用于臨界區持鎖時間比較長的操作,比如下面這些情況都可以考慮臨界區有IO操作
臨界區代碼復雜或者循環量大
臨界區競爭非常激烈
單核處理器
自旋鎖就主要用在臨界區持鎖時間非常短且CPU資源不緊張的情況下。當遞歸調用時有可能造成死鎖。
線程(節點)隊列
了解了自旋鎖之后,在學習ReentrantLock的時候,一個線程在等待鎖的時候會被封裝成一個Node節點,然后加入一個隊列中并檢測前一個節點是否是頭節點,并且嘗試獲取鎖,如果獲取鎖成功就返回,否則就阻塞。直到上一個節點釋放鎖并喚醒它。這樣看來似乎跟自旋沒什么掛鉤。這是因為AQS里面的CLH隊列是CLH隊列鎖的一種變形。先來了解一下CLH隊列鎖
CLH隊列鎖
CLH(Craig, Landin, and Hagersten locks): 是一個自旋鎖,能確保無饑餓性,提供先來先服務的公平性。
CLH鎖也是一種基于鏈表的可擴展、高性能、公平的自旋鎖,申請線程只在本地變量上自旋,它不斷輪詢前驅的狀態,如果發現前驅釋放了鎖就結束自旋。http://www.2cto.com/kf/201412/363574.html這篇文章中有比較詳細的圖解。
AQS中的CLH隊列
了解了自旋鎖與CLH隊列鎖之后,在學習AQS中的CLH隊列就比較簡單了。AQS中的CLH隊列主要是對CLH隊列鎖改動了兩個地方
1.節點結構上做出改變。CLH隊列鎖的節點包含一個布爾類型locked的字段。如果要獲取鎖,就將這個locked設置為true。然后就不停的輪訓前驅節點的locked是否釋放了鎖(這個過程我們就叫做自旋)。AQS的CLH隊列在結構上引入了頭節點,尾節點。并且擁有一個前節點與下一個節點的引用。
2.在等待獲取鎖的機制上由自旋改成了等待阻塞。
MCS
MSC與CLH最大的不同并不是鏈表是顯示還是隱式,而是線程自旋的規則不同:CLH是在前趨結點的locked域上自旋等待,而MSC是在自己的
結點的locked域上自旋等待。正因為如此,它解決了CLH在NUMA系統架構中獲取locked域狀態內存過遠的問題。
/*
* 提供了一個基于FIFO隊列,可以用于構建鎖或者其他相關同步裝置的基礎框架
* 雙向鏈表
*/
public abstract class AbstractQueuedSynchronizer
extends AbstractOwnableSynchronizer
implements java.io.Serializable {
/**
* 無參構造方法
*/
protected AbstractQueuedSynchronizer() { }
/**
* <pre>
* +------+ prev +-----+ +-----+
* head | | <---- | | <---- | | tail
* +------+ +-----+ +-----+
* </pre>
*/
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;
/** waitStatus value to indicate thread has cancelled
* 結點狀態 節點watiStatus的值
* CANCELLED,值為1,終態,該節點被取消由于超時或中斷
* SIGNAL,值為-1,表示當前節點的后繼節點包含的線程需要運行,也就是unpark,所以當前節點release或cancels時,必須unpark它的后繼節點
* CONDITION,值為-2,表示當前節點在等待condition,也就是在condition隊列中 該節點處于條件隊列中,將不會被用于sync queue,直到節點狀態被設置為0
* PROPAGATE,值為-3,表示當前場景下后續的acquireShared能夠得以執行releaseShared應該被傳播到其他節點
* 值為0,表示當前節點在sync隊列中,等待著獲取鎖
* */
static final int CANCELLED = 1;
/** waitStatus value to indicate successor's thread needs unparking */
static final int SIGNAL = -1;
/** waitStatus value to indicate thread is waiting on condition */
static final int CONDITION = -2;
/**
* waitStatus value to indicate the next acquireShared should
* unconditionally propagate
*/
static final int PROPAGATE = -3;
/**
* Status field, taking on only the values:
* SIGNAL: The successor of this node is (or will soon be)
* blocked (via park), so the current node must
* unpark its successor when it releases or
* cancels. To avoid races, acquire methods must
* first indicate they need a signal,
* then retry the atomic acquire, and then,
* on failure, block.
* CANCELLED: This node is cancelled due to timeout or interrupt.
* Nodes never leave this state. In particular,
* a thread with cancelled node never again blocks.
* CONDITION: This node is currently on a condition queue.
* It will not be used as a sync queue node
* until transferred, at which time the status
* will be set to 0. (Use of this value here has
* nothing to do with the other uses of the
* field, but simplifies mechanics.)
* PROPAGATE: A releaseShared should be propagated to other
* nodes. This is set (for head node only) in
* doReleaseShared to ensure propagation
* continues, even if other operations have
* since intervened.
* 0: None of the above
* 結點狀態
*/
volatile int waitStatus;
/**
* 前驅結點
*/
volatile Node prev;
/**
* 后繼結點
*/
volatile Node next;
/**
* 結點所對應的線程
*/
volatile Thread thread;
/**
* 下一個等待者
*/
Node nextWaiter;
/**
* 結點是否在共享模式下等待
*/
final boolean isShared() {
return nextWaiter == SHARED;
}
/**
* 獲取前驅結點,若前驅結點為空,拋出異常
*/
final Node predecessor() throws NullPointerException {
// 保存前驅結點
Node p = prev;
if (p == null) // 前驅結點為空,拋出異常
throw new NullPointerException();
else // 前驅結點不為空,返回
return p;
}
// 無參構造函數
Node() { // Used to establish initial head or SHARED marker
}
// 構造函數
Node(Thread thread, Node mode) { // Used by addWaiter
this.nextWaiter = mode;
this.thread = thread;
}
// 構造函數
Node(Thread thread, int waitStatus) { // Used by Condition
this.waitStatus = waitStatus;
this.thread = thread;
}
}
/**
* CLH隊列中頭結點
*/
private transient volatile Node head;
/**
* CLH隊列中尾結點
*/
private transient volatile Node tail;
/**
* 同步狀態
* 多線程同步獲取資源成功,則state字段會自增;若有線程釋放資源,則state字段自減。
* 信號量 記錄該線程持有鎖的次數。 該線程每次釋放所 信號量 -1。 信號量為零 代表 鎖被真正釋放
*/
private volatile int state;
/**
* @return current state value
*/
protected final int getState() {
return state;
}
/**
* @param newState the new state value
*/
protected final void setState(int newState) {
state = newState;
}
/**
* 使用unsafe的cas比較并且交換,保證原子性
*/
protected final boolean compareAndSetState(int expect, int update) {
// See below for intrinsics setup to support this
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
?? AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer。
?? 1. AbstractOwnableSynchronizer是一個抽象類一個同步器,它可能由線程獨占。該類為創建可能包含所有權概念的鎖和相關同步器提供了基礎,但是,子類和工具可以使用適當維護的值來幫助控制和監視訪問并提供診斷,實現了Serializable接口,定義了獨占模式,設置和獲取獨占模式下的線程Thread信息。
?? 2.AbstractOwnableSynchronizer實現了Serializable接口。
?? ??1)Serializable接口,序列化接口,表明該類可以被序列化,什么是序列化?簡單的說,就是能夠從類變成字節流傳輸,反序列化,就是從字節流變成原來的類。
?? 3. AbstractOwnableSynchronizer是一個抽象父類,子類有AbstractQueuedSynchronizer和AbstractQueuedLongSynchronizer,它們2個之間的區別就是異常將所有與狀態相關的參數和結果定義為long類型而不是int類型,在創建同步器(例如多級鎖和需要64位狀態的障礙)時,此類可能很有用。??
?? ??1)acquire(int arg);
?? ??以獨占模式獲取資源,如果獲取成功,直接返回,否則進去CLH等待隊列,通過自旋知道獲取到資源為止,過程中忽略線程中斷,獲取資源后才進行自我中斷(補上),下面看源碼:
/**
* AQS的獨占模式--互斥
* tryAcquire()嘗試直接去獲取資源,如果成功則直接返回;
* addWaiter()將該線程加入等待隊列的尾部,并標記為獨占模式;
* acquireQueued()使線程在等待隊列中獲取資源,一直獲取到資源后才返回。如果在整個等待過程中被中斷過,則返回true,否則返回false。
* 如果線程在等待過程中被中斷過,它是不響應的。只是獲取資源后才再進行自我中斷selfInterrupt(),將中斷補上。
*/
public final void acquire(int arg) {
if (!tryAcquire(arg) && // 再次嘗試上鎖 回到了 NonfairSync.tryAcquire 方法, tryAcquire 調用了 Sync.nonfairTryAcquire方法
acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) // 鏈表尾部添加節點 為獨占模式
selfInterrupt();
}
?? ??2)boolean tryAcquire(int arg);
?? ??嘗試以獨占的方式獲取資源,成功true,失敗false,該方法可以用于實現Lock中的tryLock()方法。
/**
* tryAcquire嘗試以獨占的方式獲取資源,如果獲取成功,則直接返回true,否則直接返回false。該方法可以用于實現Lock中的tryLock()方法。
* 該方法的默認實現是拋出UnsupportedOperationException,具體實現由自定義的擴展了AQS的同步類來實現。AQS在這里只負責定義了一個公共的方法框架。
* 這里之所以沒有定義成abstract,是因為獨占模式下只用實現tryAcquire-tryRelease,而共享模式下只用實現tryAcquireShared-tryReleaseShared。
* 如果都定義成abstract,那么每個模式也要去實現另一模式下的接口
* 由子類選擇性實現
*/
protected boolean tryAcquire(int arg) {
throw new UnsupportedOperationException();
}
?? ??3)Node addWaiter(Node mode);
?? ??將一個Node節點放入到CLH隊列的隊尾。
/**
* 將一個Node節點放入到CLH隊列的隊尾。
* 第一步:首先將oldTail賦值給newNode.prev:node.prev = pred, 把當前tail節點賦值到mode新節點的prev前一個,
* 第二步:將tail賦值給newNode:compareAndSetTail(pred, node) 把當前tail節點的內存地址修改為(指向)新的mode節點,
* 第三步:將oldTail的next指針指向newNode(即tail):pred.next = node 把當前tail節點的next后一個賦值為新的mode節點(即tail)
* 如果隊列為空,通過enq(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
//嘗試快速入隊,失敗則使用enq()方式
Node pred = tail;
if (pred != null) { // 列隊尾部不為空
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
// 列隊尾部為空 或者 CAS 操作失敗
enq(node);
return node;
}
?? ??4)boolean acquireQueued(final Node node, int arg);
?? ??使線程在等待隊列中獲取資源,一直獲取到資源后才返回。如果在整個等待過程中被中斷過,則返回true,否則返回false。
/**
* 若node節點的前繼節點是head節點,則會再次調用tryAcquire()獲取資源
* 判斷當前節點的前繼節點是否為head節點。若是,則表示該節點有資格嘗試獲取共享資源。此處的head節點的判斷在一定程度上保證資源競爭的公平性
* shouldParkAfterFailedAcquire():判斷當前節點是否可以安全進入park()
* parkAndCheckInterrupt():讓線程進入等待
* 用于隊列中的線程自旋地以獨占且不可中斷的方式獲取同步狀態(acquire),直到拿到鎖之后再返回。該方法的實現分成兩部分:
* 如果當前節點已經成為頭結點,嘗試獲取鎖(tryAcquire)成功,然后返回;否則檢查當前節點是否應該被park,然后將該線程park并且檢查當前線程是否被可以被中斷
*/
final boolean acquireQueued(final Node node, int arg) {
//標記是否成功拿到資源,默認false
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;
}
//判斷當前節點是否可以進入park,若可以,讓線程進入等待
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
//如果獲取資源失敗,則取消
if (failed)
cancelAcquire(node);
}
}
?? ??5)void selfInterrupt();
?? ??中斷當前線程
/**
* Convenience method to interrupt current thread.
* 中斷當前線程
*/
static void selfInterrupt() {
Thread.currentThread().interrupt();
}
?? ??6)Node enq(final Node node);
?? ??將當前節點插入等待隊列
/**
* 進行自旋入隊方式的enq()方法,基本和addWaiter()方法一致:
* 用于將當前節點插入等待隊列,如果隊列為空,則初始化當前隊列。整個過程以CAS自旋的方式進行,直到成功加入隊尾為止
*/
private Node enq(final Node node) {
for (;;) {
Node t = tail;
if (t == null) { // Must initialize 必須初始化 尾部為空 嘗試構建表結構
if (compareAndSetHead(new Node()))
tail = head;
} else { //尾部不為空 不斷嘗試 CAS 操作
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
?? ??7)boolean compareAndSetHead(Node update);
?? ??通過原子(CAS)操作 改變上鎖狀態
/**
* CAS head field. Used only by enq.
* 通過原子操作 改變上鎖狀態
* this == null
* 第一個參數為需要改變的對象,第二個為偏移量(即之前求出來的valueOffset的值),第三個參數為期待的值,第四個為更新后的值
*/
private final boolean compareAndSetHead(Node update) {
//調用本地方法 實現硬件級別的原子操作 cas
return unsafe.compareAndSwapObject(this, headOffset, null, update);
}
?? ??8)boolean parkAndCheckInterrupt();
?? ??讓線程去休息,真正進入等待狀態
? ?
/**
* 該方法讓線程去休息,真正進入等待狀態。park()會讓當前線程進入waiting狀態。在此狀態下,有兩種途徑可以喚醒該線程:
* 1)被unpark();2)被interrupt()。需要注意的是,Thread.interrupted()會清除當前線程的中斷標記位
*/
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this); // 又是一個底層類 實現線程等待
return Thread.interrupted();
}
?? ??9)boolean shouldParkAfterFailedAcquire(Node pred, Node node);
?? ??判斷當前節點中的線程,是否可以安全的進入park()。返回true,表示進程可以進入park
/**
* 該方法的作用在于判斷當前節點中的線程,是否可以安全的進入park()。返回true,表示進程可以進入park。若前驅節點的waitStatus為SIGNAL,則表示當前節點可以安全的park()。
*/
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;
//如果前驅節點的waitStatus為SIGNAL -1,則表示當前節點可以安全的park()
if (ws == Node.SIGNAL)
/*
* This node has already set status asking a release
* to signal it, so it can safely park.
*/
return true;
// waitStatus>0,即為CANCELLED狀態,此時當前節點需要找到狀態不為CANCELLED狀態的節點,將其設置為自己的前驅節點,并將新的前驅節點的next指向自己。
// 注意,這樣做完之后,那些當前節點的waitStatus狀態為CANCELLED的前驅節點鏈,將成為孤鏈。但這個孤鏈仍然有指向原等待隊列的prev和next指針。只是原等待隊列中已經沒有指向孤鏈的節點指針
// 將前驅節點移出列隊
if (ws > 0) {
/*
* Predecessor was cancelled. Skip over predecessors and
* indicate retry.
*/
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
/*
* waitStatus must be 0 or PROPAGATE. Indicate that we
* need a signal, but don't park yet. Caller will need to
* retry to make sure it cannot acquire before parking.
*/
// 走到此處,表明前驅節點的狀態為0或PROPAGATE。此時可以將前驅節點的waitStatus設置為SIGNAL狀態
// 注意:這里仍然要返回false,表明當前節點不能被park。我們需要在park之前,重試確認該節點不能獲取到資源
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
?? ??1)acquireShared(int arg);
?? ??以共享模式獲取資源,如果獲取成功,直接返回,否則進去CLH等待隊列,通過自旋知道獲取到資源為止,過程中忽略線程中斷,獲取資源后才進行自我中斷(補上),下面看源碼:
/**
* aqs的共享模式
* 獲取指定量的資源,獲取成功則直接返回,獲取失敗則進入等待隊列,直到獲取到資源為止,整個過程忽略中斷
*/
public final void acquireShared(int arg) {
if (tryAcquireShared(arg) < 0)
doAcquireShared(arg);
}
?? ??2)int tryAcquireShared(int arg);
?? ??嘗試以共享的方式獲取資源,成功true,失敗false,該方法可以用于實現Lock中的tryLock()方法。
/**
* tryAcquireShared嘗試以共享的方式獲取資源,如果獲取成功,則直接返回true,否則直接返回false。該方法可以用于實現Lock中的tryLock()方法。
* 該方法的默認實現是拋出UnsupportedOperationException,具體實現由自定義的擴展了AQS的同步類來實現。AQS在這里只負責定義了一個公共的方法框架。
* 這里之所以沒有定義成abstract,是因為獨占模式下只用實現tryAcquire-tryRelease,而共享模式下只用實現tryAcquireShared-tryReleaseShared。
* 如果都定義成abstract,那么每個模式也要去實現另一模式下的接口
*/
protected int tryAcquireShared(int arg) {
throw new UnsupportedOperationException();
}
?? ??3)doAcquireShared(int arg);
?? ??將當前線程加入等待隊列尾部休息,直到其他線程釋放資源喚醒自己,自己成功拿到相應量的資源后才返回
/**
* Acquires in shared uninterruptible mode.
* @param arg the acquire argument
* 將當前線程加入等待隊列尾部休息,直到其他線程釋放資源喚醒自己,自己成功拿到相應量的資源后才返回
*/
private void doAcquireShared(int arg) {
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
if (p == head) {
int r = tryAcquireShared(arg);
if (r >= 0) {
setHeadAndPropagate(node, r);
p.next = null; // help GC
if (interrupted)
selfInterrupt();
failed = false;
return;
}
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
?? ??4)boolean parkAndCheckInterrupt();
?? ??讓線程去休息,真正進入等待狀態
/**
* 該方法讓線程去休息,真正進入等待狀態。park()會讓當前線程進入waiting狀態。在此狀態下,有兩種途徑可以喚醒該線程:
* 1)被unpark();2)被interrupt()。需要注意的是,Thread.interrupted()會清除當前線程的中斷標記位
*/
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this); // 又是一個底層類 實現線程等待
return Thread.interrupted();
}
?? ??5)cancelAcquire(Node node);
?? ??取消節點
/**
* 取消節點
* 列隊等待中 拋出異常會調用此方法
*/
private void cancelAcquire(Node node) {
// Ignore if node doesn't exist
if (node == null)
return;
//找到適合的前繼節點,當前節點的waitStatus賦值為CANCELLED
node.thread = null; // 釋放線程
// Skip cancelled predecessors 前驅節點已被取消 重新定義前驅節點
Node pred = node.prev;
//若前繼節點是CANCELLED,則繼續找前繼節點,直至找到一個正常的前繼節點賦值給node,作為node的新前繼節點
while (pred.waitStatus > 0)
node.prev = pred = pred.prev;
Node predNext = pred.next;
node.waitStatus = Node.CANCELLED; // 取消當前線程 所屬的節點(標記為取消), 沒有使用 cas 因為 其他線程 不會干擾這里
// If we are the tail, remove ourselves.
//特殊情況:node==tail節點,將pred作為tail節點,然后將cancelledNodes節點鏈從CLH隊列剔除
if (node == tail && compareAndSetTail(node, pred)) {
compareAndSetNext(pred, predNext, null);
} else {
// If successor needs signal, try to set pred's next-link
// so it will get one. Otherwise wake it up to propagate.
int ws;
//正常情況:則將cancelledNodes節點鏈從CLH隊列剔除
if (pred != head &&
((ws = pred.waitStatus) == Node.SIGNAL ||
(ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) &&
pred.thread != null) {
Node next = node.next;
if (next != null && next.waitStatus <= 0)
compareAndSetNext(pred, predNext, next);
} else {
//特殊情況:如果node是head的后繼節點,則直接喚醒node的后繼節點 pred==head節點:嘗試調用unparkSuccessor(node),嘗試喚醒當前節點的后繼節點
unparkSuccessor(node);
}
node.next = node; // help GC
}
}
?? ??6)unparkSuccessor(Node node);
?? ??喚醒后繼節點
/**
* 喚醒后繼節點
* 注意:如果當前節點的后繼節點為空,或者是被取消的節點。那就從tail節點逆向遍歷CLH隊列,直至找到一個距離當前節點node最近,且waitStatus<=0的節點,然后喚醒該節點
*/
private void unparkSuccessor(Node node) {
/*
* If status is negative (i.e., possibly needing signal) try
* to clear in anticipation of signalling. It is OK if this
* fails or if status is changed by waiting thread.
*/
int ws = node.waitStatus; //獲取頭結點的等待狀態
if (ws < 0) //把該狀態設置成0
compareAndSetWaitStatus(node, ws, 0);
/*
* 若后繼節點不符合喚醒標準,則逆向遍歷CLH,直至找到一個距離當前節點node最近,且waitStatus<=0的節點
*/
Node s = node.next; //找到后繼節點,喚醒后繼節點
if (s == null || s.waitStatus > 0) { //很不巧,后繼節點,節點為null,或者被取消
s = null;
for (Node t = tail; t != null && t != node; t = t.prev) //這里采用反向遍歷因為是雙向鏈表
if (t.waitStatus <= 0) //找到實際未被取消的節點
s = t;
}
if (s != null)
LockSupport.unpark(s.thread); //喚醒節點
}
?? ??1)boolean release(int arg);
?? ??獨占模式釋放資源
/**
* 資源的釋放
* 調用tryRelease方法進行釋放鎖
* 釋放鎖成功后,獲取頭節點,接著喚醒后繼節點,調用unparkSuccessor方法
*/
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
?? ??2)boolean tryRelease(int arg);
?? ??獨占模式下嘗試釋放鎖,由子類選擇性實現
protected boolean tryRelease(int arg) {
throw new UnsupportedOperationException();
}
?? ??3)boolean releaseShared(int arg);
?? ??共享模式釋放資源
/**
* Releases in shared mode. Implemented by unblocking one or more
* threads if {@link #tryReleaseShared} returns true.
*
* @param arg the release argument. This value is conveyed to
* {@link #tryReleaseShared} but is otherwise uninterpreted
* and can represent anything you like.
* @return the value returned from {@link #tryReleaseShared}
*/
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
?? ??4)boolean tryReleaseShared(int arg);
?? ??共享模式下嘗試釋放鎖,由子類選擇性實現
protected boolean tryReleaseShared(int arg) {
throw new UnsupportedOperationException();
}
?? ??5)int fullyRelease(Node node);
?? ??使用當前節點狀態調用release,成功返回狀態,失敗跑出異常
/**
* 使用當前節點狀態調用release,成功返回狀態,失敗跑出異常
*/
final int fullyRelease(Node node) {
boolean failed = true;
try {
int savedState = getState();
if (release(savedState)) {
failed = false;
return savedState;
} else {
throw new IllegalMonitorStateException();
}
} finally {
if (failed)
node.waitStatus = Node.CANCELLED;
}
}
/**
* Unsafe類實例
*/
private static final Unsafe unsafe = Unsafe.getUnsafe();
/** state內存偏移地址 */
private static final long stateOffset;
/** head內存偏移地址 */
private static final long headOffset;
/** tail內存偏移地址 */
private static final long tailOffset;
private static final long waitStatusOffset;
/** next內存偏移地址 */
private static final long nextOffset;
//靜態初始化塊
static {
try {
// 獲取偏移量
stateOffset = unsafe.objectFieldOffset
(AbstractQueuedSynchronizer.class.getDeclaredField("state"));
headOffset = unsafe.objectFieldOffset
(AbstractQueuedSynchronizer.class.getDeclaredField("head"));
tailOffset = unsafe.objectFieldOffset
(AbstractQueuedSynchronizer.class.getDeclaredField("tail"));
waitStatusOffset = unsafe.objectFieldOffset
(Node.class.getDeclaredField("waitStatus"));
nextOffset = unsafe.objectFieldOffset
(Node.class.getDeclaredField("next"));
} catch (Exception ex) { throw new Error(ex); }
}
/**
* CAS head field. Used only by enq.
* 通過原子操作 改變上鎖狀態
* this == null
* 第一個參數為需要改變的對象,第二個為偏移量(即之前求出來的valueOffset的值),第三個參數為期待的值,第四個為更新后的值
*/
private final boolean compareAndSetHead(Node update) {
//調用本地方法 實現硬件級別的原子操作 cas
return unsafe.compareAndSwapObject(this, headOffset, null, update);
}
/**
* CAS tail field. Used only by enq.
*/
private final boolean compareAndSetTail(Node expect, Node update) {
return unsafe.compareAndSwapObject(this, tailOffset, expect, update);
}
/**
* CAS waitStatus field of a node.
*/
private static final boolean compareAndSetWaitStatus(Node node,
int expect,
int update) {
return unsafe.compareAndSwapInt(node, waitStatusOffset,
expect, update);
}
/**
* CAS next field of a node.
*/
private static final boolean compareAndSetNext(Node node,
Node expect,
Node update) {
return unsafe.compareAndSwapObject(node, nextOffset, expect, update);
}
內部類ConditionObject,它實現了Condition接口,主要用于實現條件鎖。
ConditionObject中也維護了一個隊列,這個隊列主要用于等待條件的成立,當條件成立時,其它線程將signal這個隊列中的元素,將其移動到CLH的隊列中,等待占有鎖的線程釋放鎖后被喚醒。
Condition典型的運用場景是在BlockingQueue中的實現,當隊列為空時,獲取元素的線程阻塞在notEmpty條件上,一旦隊列中添加了一個元素,將通知notEmpty條件,將其隊列中的元素移動到AQS隊列中等待被喚醒。
/**
* 構造一個條件隊列,來等待條件是否為真
*/
public class ConditionObject implements Condition, java.io.Serializable {
/** 版本號 */
private static final long serialVersionUID = 1173984872572414699L;
/** First node of condition queue. condition隊列的頭結點 */
private transient Node firstWaiter;
/** Last node of condition queue. condition隊列的尾結點 */
private transient Node lastWaiter;
/**
* Creates a new {@code ConditionObject} instance.
* 構造函數
*/
public ConditionObject() { }
/**
* Adds a new waiter to wait queue.
* @return its new wait node
* 添加新的waiter到wait隊列
*/
private Node addConditionWaiter() {
//保存尾結點
Node t = lastWaiter;
// If lastWaiter is cancelled, clean out. 尾結點不為空,并且尾結點的狀態不為CONDITION
if (t != null && t.waitStatus != Node.CONDITION) {
//清除狀態為CONDITION的結點
unlinkCancelledWaiters();
//將最后一個結點重新賦值給t
t = lastWaiter;
}
Node node = new Node(Thread.currentThread(), Node.CONDITION);
if (t == null)
firstWaiter = node;
else
t.nextWaiter = node;
lastWaiter = node;
return node;
}
1)AQS分為獨占鎖和共享鎖。
2)AQS分為CLH自旋隊列和Condition條件隊列。
3)AQS是一個雙向鏈表,由state狀態控制。
4)AQS由volatile修飾保證多線程可見,采用CAS保證原子性。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。