什麼是AQS
- AbstractQueuedSynchronizer是一個佇列同步器,是用來構建鎖和其它同步元件的基礎框架,它使用一個volatile修飾的int成員變數表示同步狀態,通過內建的FIFO佇列來完成資源獲取執行緒排隊的工作
- 通過改變int成員變數state來表示鎖是否獲取成功,當state>0表示鎖獲取成功,當state=0時說明鎖釋放成功。提供了三個方法(
getState()
、setState(int newState)
、compareAndSetState(int expect,int update)
)來對同步狀態state進行操作,AQS確保對state操作時執行緒安全的。 - 主要使用方式是繼承,子類通過繼承同步器並實現它的抽像方法來管理同步狀態。
- 提供獨佔式和共享式兩種方式來操作同步狀態的獲取與釋放
- ReentrantLock、ReentrantReadWriteLock、Semaphore等就併發工具就是基於護一個內部幫助器類整合AQS來實現的的
AQS提供的方法(列出主要幾個)
acquire(int arg)
以獨佔模式獲取物件,忽略中斷。acquireInterruptibly(int arg)
以獨佔模式獲取物件,如果被中斷則中止。acquire(int arg)
以獨佔模式獲取物件,忽略中斷。acquireShared(int arg)
以共享模式獲取物件,忽略中斷。acquireSharedInterruptibly(int arg)
以共享模式獲取物件,如果被中斷則中止。compareAndSetState(int expect, int update)
如果當前狀態值等於預期值,則以原子方式將同步狀態設定為給定的更新值。getState()
返回同步狀態的當前值。release(int arg)
以獨佔模式釋放物件。releaseShared(int arg)
以共享模式釋放物件。setState(int newState)
設定同步狀態的值。tryAcquire(int arg)
試圖在獨佔模式下獲取物件狀態。tryAcquireNanos(int arg, long nanosTimeout)
試圖以獨佔模式獲取物件,如果被中斷則中止,如果到了給定超時時間,則會失敗。tryAcquireShared(int arg)
試圖在共享模式下獲取物件狀態。tryAcquireSharedNanos(int arg, long nanosTimeout)
試圖以共享模式獲取物件,如果被中斷則中止,如果到了給定超時時間,則會失敗。tryReleaseShared(int arg)
試圖設定狀態來反映共享模式下的一個釋放。
佇列同步器的實現分析
- 同步器依賴內部的同步佇列(一個FIFO雙向佇列)來完成同步狀態的管理,當前執行緒獲取同步狀態失敗時,同步器會將當前執行緒以及等待狀態等資訊構造成為一個節點(Node)並將其加入同步佇列,同時會阻塞當前執行緒,當同步狀態釋放時,會把首節點中的執行緒喚醒,使其再次嘗試獲取同步狀態。
static final class Node {
/** 表示節點正在共享模式中等待 */
static final Node SHARED = new Node();
/** 表示節點正在獨佔模式下等待 */
static final Node EXCLUSIVE = null;
/** 表示取消狀態,同步佇列中等待的執行緒等待超時或中斷,需要從同步佇列中取消等待,節點進入該值不會發生變化 */
static final int CANCELLED = 1;
/** 後續節點的執行緒處於等待狀態,而當前節點的執行緒如果釋放了同步狀態或者取消,將會通知後續節點執行*/
static final int SIGNAL = -1;
/** 節點在等待中,節點執行緒等待在Conditions上。當其他執行緒對Condition呼叫了signal()後,該節點將會從等待佇列中轉移到同步佇列中,加入到同步狀態的獲取中 */
static final int CONDITION = -2;
/**
* 表示下一次共享式同步狀態獲取將會無條件傳播下去
*/
static final int PROPAGATE = -3;
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;
}
}
複製程式碼
節點是構成同步佇列的基礎,同步器擁有首節點(head)和尾節點(tail),沒有成功獲取同步狀態的執行緒將會成為節點加入該佇列的尾部,同步佇列的
同步器的acquire方法(獲取)
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
複製程式碼
- 呼叫自定義同步器實現的tryAcquire(int arg)方法,該方法保證執行緒安全的獲取同步狀態,如果同步狀態獲取失敗,則構造同步節點並通過addWaiter(Node node)方法將該節點加入到同步佇列的尾部,最後呼叫acquireQueued(Node node,int arg)方法,使得該 節點以"死迴圈"(自旋)的方式獲取同步狀態。
獨佔式的獲取與釋放總結
- 在獲取同步狀態時,同步器維護一個同步佇列,獲取狀態失敗的執行緒都會被加入到佇列中並在佇列中進行自旋;移出佇列(或停止自旋)的條件是前驅節點為頭節點且成功獲取了同步狀態。在釋放同步狀態時,同步器呼叫tryRelease(int arg)方法釋放同步狀態,然後喚醒頭節點的後繼節點
同步器的release方法(釋放)
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
複製程式碼
- 該方法執行時,會喚醒頭節點的後繼節點執行緒,unparkSuccessor(Node node)方法使用LockSupport來喚醒處於等待狀態的執行緒。
基於AQS實現一個簡單的可重入的獨佔式鎖的獲取與釋放
package com.example.juc;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
/**
* 基於AQS實現一個簡單的鎖
*
* @author qinxuewu
* @create 19/3/18下午11:44
* @since 1.0.0
*/
public class MyAQSLock implements Lock {
private final MySync sync=new MySync();
/**
* 構建一個內部幫助器 整合AQS
*/
private static class MySync extends AbstractQueuedSynchronizer{
//狀態為0時獲取鎖,
/***
* 一個執行緒進來時,如果狀態為0,就更改state變數,返回true表示拿到鎖
*
* 當state大於0說明當前鎖已經被持有,直接返回false,如果重複進來,就累加state,返回true
* @param arg
* @return
*/
@Override
protected boolean tryAcquire(int arg) {
//獲取同步狀態狀態的成員變數的值
int state=getState();
Thread cru=Thread.currentThread();
if(state==0){
//CAS方式更新state,保證原子性,期望值,更新的值
if( compareAndSetState(0,arg)){
//設定成功
//設定當前執行緒
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
}else if(Thread.currentThread()==getExclusiveOwnerThread()){
//如果還是當前執行緒進來,累加state,返回true 可重入
setState(state+1);
return true;
}
return false;
}
/**
* 釋放同步狀態
* @param arg
* @return
*/
@Override
protected boolean tryRelease(int arg) {
boolean flag=false;
//判斷釋放操作是否是當前執行緒,
if(Thread.currentThread()==getExclusiveOwnerThread()){
//獲取同步狀態成員變數,如果大於0 才釋放
int state=getState();
if(getState()==0){
//當前執行緒置為null
setExclusiveOwnerThread(null);
flag=true;
}
setState(arg);
}else{
//不是當執行緒丟擲異常
throw new RuntimeException();
}
return flag;
}
Condition newCondition(){
return new ConditionObject();
}
}
@Override
public void lock() {
sync.acquire(1);
}
@Override
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
/**
* 加鎖
* @return
*/
@Override
public boolean tryLock() {
return sync.tryAcquire(1);
}
@Override
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
return sync.tryAcquireNanos(1,unit.toNanos(time));
}
/**
* 釋放鎖
*/
@Override
public void unlock() {
sync.tryRelease(1);
}
@Override
public Condition newCondition() {
return sync.newCondition();
}
}
複製程式碼
測試
public class MyAQSLockTest {
MyAQSLock lock=new MyAQSLock();
private int i=0;
public int next() {
try {
lock.lock();
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
return i++;
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
return 0;
}
public void test1(){
System.out.println("test1");
test2();
}
public void test2(){
System.out.println("test2");
}
public static void main(String[] args){
MyAQSLockTest test=new MyAQSLockTest();
// Thread thread = new Thread(new Runnable() {
// @Override
// public void run() {
// while (true) {
//
// System.out.println(Thread.currentThread().getName() + "-" + test.next());
//
// }
//
// }
// });
// thread.start();
//
// Thread thread2 = new Thread(new Runnable() {
// @Override
// public void run() {
// while (true) {
//
// System.out.println(Thread.currentThread().getName() + "-" + test.next());
//
// }
//
// }
// });
// thread2.start();
//可重複鎖演示
Thread thread3 = new Thread(new Runnable() {
@Override
public void run() {
test.test1();
}
});
thread3.start();
}
}
複製程式碼
- 參考併發程式設計的藝術
- JDK8原始碼
- 我的部落格:blog.qinxuewu.club/
- Github: github.com/a870439570