Java併發之AQS原理詳解

2021-08-18 21:54:34 字數 4708 閱讀 4066

一、abstractqueuedsynchronizer

的用途( 下面簡稱

aqs,jdk 1.8)

aqs是借助

fifo等待佇列,用來實現同步器的同步框架,通俗的來說,它是用來實現鎖的工具,一般來說,它需要實現這些功能:

二、aqs

的工作原理

aqs的核心是基於鍊錶實現的

fifo等待佇列,該佇列用於存放需要等待的執行緒節點,

aqs中

node內部類用於儲存等待執行緒的相關資訊。

1、  等待佇列結構

工作機制:

aqs的等待佇列是基於鍊錶實現的

fifo

的等待佇列,佇列每個節點只關心其前驅節點的狀態,執行緒喚醒時只喚醒隊頭等待執行緒(即

head

的後繼節點,並且等待狀態不大於0)。

2、node內部類

node內部類用於儲存等待執行緒的相關資訊

主要的資料成員有:

static final node shared;//表明該節點在共享模式下等待;

static final node exclusive;//表明該節點在互斥模式下等待;

volatile int waitstatus;//該節點的等待狀態:1 cancelled,-1 signal, -2 condition, -3 propagate, 0 others;

volatile node prev;//該節點的前驅節點

volatile node next;//該節點的後繼節點

volatile thread thread;//該節點關聯的執行緒

主要的方法成員有:

final boolean isshared();//當前節點是否在共享模式下等待;

final node predecessor();// 返回當前節點的前驅節點;

3、aqs的資料成員

private transient volatile node head;//等待佇列的頭節點,該節點的等待狀態不為cancelled;

private transient volatile node tail;//等待佇列的尾節點;

private volatile int state;//同步器的狀態;

4、aqs方法成員之節點基本操作

注:只寫了一些比較主要的,像sethead(), hasqueuedthreads(), hascontented(), ….可以直接檢視原始碼,比較簡單

將執行緒作放入等待佇列中

private node addwaiter(node mode);

功能:以指定模式建立當前執行緒的node,並新增到等待佇列中,返回新增的節點。

具體操作:

原始碼:private node addwaiter(node mode)

} enq(node);

return node;

}private node enq(final node node);

功能:將node新增到等待佇列中,一直迴圈直到新增成功

具體操作:

原始碼:private node enq(final node node) else

} }}喚醒給定節點的後繼節點

private void unparksuccessor(node node);

具體操作:

原始碼:private void unparksuccessor(node node)

if (s != null)

locksupport.unpark(s.thread);

}取消獲取鎖

private void cancelacquire(node node);

具體操作:

原始碼:private void cancelacquire(node node) else else

node.next = node; // help gc

} }private static booleanshouldparkafte***iledacquire(node pred, node node);

功能:獲取鎖失敗後檢查並更新節點的狀態,如果執行緒應該阻塞的話,返回true

具體操作:

原始碼:private static boolean shouldparkafte***iledacquire(node pred, node node) while (pred.waitstatus > 0);

pred.next = node;

} else

return false;

}注:對於該方法的功能,我是這樣理解的,

應用場景:

該方法經常用於阻塞執行緒的方法中(比如acquirequeued(finalnode node, int arg), doacquireinterruptibly(int arg)等),並且經常在for死迴圈中重複呼叫。

情形一:如果node節點的前驅節點的等待狀態為signal,說明前驅節點正在等待獲取鎖,node節點肯定不能直接獲取鎖的,必須等待,因此可以安全阻塞,返回true,可以在阻塞執行緒的方法中將當前執行緒掛起;

情形二:如果node節點的前驅節點的等待狀態為cancelled,更新node節點的前驅節點(跳過取消的前驅節點),返回false。更新完成後,node節點的前驅節點有可能就是head節點,即node節點可以直接嘗試獲取鎖,因此在退出該方法後在阻塞執行緒的方法中重新執行迴圈;

情形三:否則的話,node節點的前驅節點的等待狀態為0或者propagate,將前驅節點的等待狀態更新為signal,返回false後,在阻塞執行緒的方法中重新執行迴圈就可以達到情形一。

private final booleanparkandcheckinterrupt()

功能:park乙個執行緒,當執行緒被unparkh後,返回中斷狀態

原始碼:private final boolean parkandcheckinterrupt()

方法很簡單,簡要說一下:

5、aqs方法成員之獲取鎖的操作

說明:因為原理都差不多,因此主要講兩個方法:非中斷模式下獨佔式獲取鎖public final void acquire(int arg)和中斷模式下獨佔式獲取鎖publicfinal void acquireinterruptibly(int arg):

非中斷模式下獨佔式獲取鎖

public final void acquire(int arg)

具體操作:

原始碼:public final void acquire(int arg)

final boolean acquirequeued(final node node, int arg)

功能:非中斷模式下獨佔式獲取鎖,返回值為等待獲取鎖的過程中該執行緒是否被中斷

具體操作:

解釋:該方法會阻塞執行緒,直到執行緒成功獲得鎖,返回

interrupted

。期間如果遇到中斷的話,僅僅是將

interrupted

值置為true

。也就是說不論是否中斷,等待佇列中的

執行緒不可以取消獲取鎖,獲取鎖成功後才會從等待佇列中移除。

原始碼:final boolean acquirequeued(final node node, int arg)

if (shouldparkafte***iledacquire(p, node) &&

parkandcheckinterrupt())

interrupted = true;

} } finally

}中斷模式下獨佔式獲取鎖

public final void acquireinterruptibly(int arg)

具體操作:

原始碼:public final void acquireinterruptibly(int arg)

throws interruptedexception

private voiddoacquireinterruptibly(int arg)

功能:執行緒以可中斷的互斥模式下獲取鎖

具體操作:

解釋:該方法會阻塞執行緒,執行緒要想從該方法中返回的有兩種途徑,一是成功獲取鎖,二是遇到中斷,如果入到中斷的話會取消獲取鎖。

6、aqs方法成員之釋放鎖的操作

釋放鎖分類同獲取鎖,獨佔式地釋放鎖呼叫tryrelease()方法,共享式釋放鎖呼叫tryreleaseshared()方法,共享式釋放鎖會一直嘗試釋放鎖,直到釋放成功;獨佔式釋放鎖不一定可以成功釋放鎖,釋放成功返回true,失敗返回false。

原始碼不講了,思路很簡單,通過呼叫unparksuccessor(node node)方法喚醒等待佇列中最靠前且未取消的節點。

7、aqs方法成員之必須重寫的方法

protected boolean tryacquire(int arg);

protected int tryacquireshared(int arg)

protected boolean tryrelease(int arg);

protected boolean tryreleaseshared(int arg);

protected boolean isheldexclusively()。

三、aqs

的執行流程

以獲取鎖為例:

併發程式設計核心框架AQS之獨佔鎖原理詳解

如何設計符合冪等性的高質量restful api 理解restful的冪等性,並且設計符合冪等規範的高質量restful api。http冪等方法,是指無論呼叫多少次都不會有不同結果的 http 方法。不管你呼叫一次,還是呼叫一百次,一千次,結果都是相同的。還是以之前的博文的例子為例。get tic...

java併發之AQS共享模式原始碼解讀

共享鎖的乙個實現類就是訊號量semaphore,semaphores一般用於對某種訪問資源的限制,乙個訊號量相當於持有一些許可 permits 執行緒可以呼叫semaphore物件的acquire 方法獲取乙個許可,呼叫release 來歸還乙個許可。同樣semaphore有公平和非公平兩種模式。預...

併發程式設計之AQS的同步原理

aqs abstractqueuedsynchronizer 抽象佇列同步器。aqs的同步狀態 aqs使用乙個int成員變數來表示同步狀態,通過內建的fifo佇列來完成獲取資源執行緒的排隊工作。aqs使用cas對該同步狀態進行原子操作實現對其值的修改。private volatile int sta...