(1)AQS是什么?
創(chuàng)新互聯(lián)公司專注于武昌企業(yè)網(wǎng)站建設(shè),成都響應(yīng)式網(wǎng)站建設(shè)公司,商城網(wǎng)站開發(fā)。武昌網(wǎng)站建設(shè)公司,為武昌等地區(qū)提供建站服務(wù)。全流程定制網(wǎng)站設(shè)計,專業(yè)設(shè)計,全程項目跟蹤,創(chuàng)新互聯(lián)公司專業(yè)和態(tài)度為您提供的服務(wù)(2)AQS的定位?
(3)AQS的實現(xiàn)原理?
(4)基于AQS實現(xiàn)自己的鎖?
AQS的全稱是AbstractQueuedSynchronizer,它的定位是為Java中幾乎所有的鎖和同步器提供一個基礎(chǔ)框架。
AQS是基于FIFO的隊列實現(xiàn)的,并且內(nèi)部維護了一個狀態(tài)變量state,通過原子更新這個狀態(tài)變量state即可以實現(xiàn)加鎖解鎖操作。
本章及后續(xù)章節(jié)的內(nèi)容理解起來可能會比較晦澀,建議先閱讀彤哥上一章的內(nèi)容【死磕 java同步系列之自己動手寫一個鎖Lock】。
static final class Node {
// 標識一個節(jié)點是共享模式
static final Node SHARED = new Node();
// 標識一個節(jié)點是互斥模式
static final Node EXCLUSIVE = null;
// 標識線程已取消
static final int CANCELLED = 1;
// 標識后繼節(jié)點需要喚醒
static final int SIGNAL = -1;
// 標識線程等待在一個條件上
static final int CONDITION = -2;
// 標識后面的共享鎖需要無條件的傳播(共享鎖需要連續(xù)喚醒讀的線程)
static final int PROPAGATE = -3;
// 當前節(jié)點保存的線程對應(yīng)的等待狀態(tài)
volatile int waitStatus;
// 前一個節(jié)點
volatile Node prev;
// 后一個節(jié)點
volatile Node next;
// 當前節(jié)點保存的線程
volatile Thread thread;
// 下一個等待在條件上的節(jié)點(Condition鎖時使用)
Node nextWaiter;
// 是否是共享模式
final boolean isShared() {
return nextWaiter == SHARED;
}
// 獲取前一個節(jié)點
final Node predecessor() throws NullPointerException {
Node p = prev;
if (p == null)
throw new NullPointerException();
else
return p;
}
// 節(jié)點的構(gòu)造方法
Node() { // Used to establish initial head or SHARED marker
}
// 節(jié)點的構(gòu)造方法
Node(Thread thread, Node mode) { // Used by addWaiter
// 把共享模式還是互斥模式存儲到nextWaiter這個字段里面了
this.nextWaiter = mode;
this.thread = thread;
}
// 節(jié)點的構(gòu)造方法
Node(Thread thread, int waitStatus) { // Used by Condition
// 等待的狀態(tài),在Condition中使用
this.waitStatus = waitStatus;
this.thread = thread;
}
}
典型的雙鏈表結(jié)構(gòu),節(jié)點中保存著當前線程、前一個節(jié)點、后一個節(jié)點以及線程的狀態(tài)等信息。
// 隊列的頭節(jié)點
private transient volatile Node head;
// 隊列的尾節(jié)點
private transient volatile Node tail;
// 控制加鎖解鎖的狀態(tài)變量
private volatile int state;
定義了一個狀態(tài)變量和一個隊列,狀態(tài)變量用來控制加鎖解鎖,隊列用來放置等待的線程。
注意,這幾個變量都要使用volatile關(guān)鍵字來修飾,因為是在多線程環(huán)境下操作,要保證它們的值修改之后對其它線程立即可見。
這幾個變量的修改是直接使用的Unsafe這個類來操作的:
// 獲取Unsafe類的實例,注意這種方式僅限于jdk自己使用,普通用戶是無法這樣調(diào)用的
private static final Unsafe unsafe = Unsafe.getUnsafe();
// 狀態(tài)變量state的偏移量
private static final long stateOffset;
// 頭節(jié)點的偏移量
private static final long headOffset;
// 尾節(jié)點的偏移量
private static final long tailOffset;
// 等待狀態(tài)的偏移量(Node的屬性)
private static final long waitStatusOffset;
// 下一個節(jié)點的偏移量(Node的屬性)
private static final long nextOffset;
static {
try {
// 獲取state的偏移量
stateOffset = unsafe.objectFieldOffset
(AbstractQueuedSynchronizer.class.getDeclaredField("state"));
// 獲取head的偏移量
headOffset = unsafe.objectFieldOffset
(AbstractQueuedSynchronizer.class.getDeclaredField("head"));
// 獲取tail的偏移量
tailOffset = unsafe.objectFieldOffset
(AbstractQueuedSynchronizer.class.getDeclaredField("tail"));
// 獲取waitStatus的偏移量
waitStatusOffset = unsafe.objectFieldOffset
(Node.class.getDeclaredField("waitStatus"));
// 獲取next的偏移量
nextOffset = unsafe.objectFieldOffset
(Node.class.getDeclaredField("next"));
} catch (Exception ex) { throw new Error(ex); }
}
// 調(diào)用Unsafe的方法原子更新state
protected final boolean compareAndSetState(int expect, int update) {
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
關(guān)于Unsafe類的講解請參考彤哥之前寫的【死磕 java魔法類之Unsafe解析】。
我們可以看到AQS的全稱是AbstractQueuedSynchronizer,它本質(zhì)上是一個抽象類,說明它本質(zhì)上應(yīng)該是需要子類來實現(xiàn)的,那么子類實現(xiàn)一個同步器需要實現(xiàn)哪些方法呢?
// 互斥模式下使用:嘗試獲取鎖
protected boolean tryAcquire(int arg) {
throw new UnsupportedOperationException();
}
// 互斥模式下使用:嘗試釋放鎖
protected boolean tryRelease(int arg) {
throw new UnsupportedOperationException();
}
// 共享模式下使用:嘗試獲取鎖
protected int tryAcquireShared(int arg) {
throw new UnsupportedOperationException();
}
// 共享模式下使用:嘗試釋放鎖
protected boolean tryReleaseShared(int arg) {
throw new UnsupportedOperationException();
}
// 如果當前線程獨占著鎖,返回true
protected boolean isHeldExclusively() {
throw new UnsupportedOperationException();
}
問題:這幾個方法為什么不直接定義成抽象方法呢?
因為子類只要實現(xiàn)這幾個方法中的一部分就可以實現(xiàn)一個同步器了,所以不需要定義成抽象方法。
下面我們通過一個案例來介紹AQS中的部分方法。
直接上代碼:
public class MyLockBaseOnAqs {
// 定義一個同步器,實現(xiàn)AQS類
private static class Sync extends AbstractQueuedSynchronizer {
// 實現(xiàn)tryAcquire(acquires)方法
@Override
public boolean tryAcquire(int acquires) {
if (compareAndSetState(0, 1)) {
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
// 實現(xiàn)tryRelease(releases)方法
@Override
protected boolean tryRelease(int releases) {
setExclusiveOwnerThread(null);
setState(0);
return true;
}
}
// 聲明同步器
private final Sync sync = new Sync();
// 加鎖
public void lock() {
sync.acquire(1);
}
// 解鎖
public void unlock() {
sync.release(1);
}
private static int count = 0;
public static void main(String[] args) throws InterruptedException {
MyLockBaseOnAqs lock = new MyLockBaseOnAqs();
CountDownLatch countDownLatch = new CountDownLatch(1000);
IntStream.range(0, 1000).forEach(i -> new Thread(() -> {
lock.lock();
try {
IntStream.range(0, 10000).forEach(j -> {
count++;
});
} finally {
lock.unlock();
}
// System.out.println(Thread.currentThread().getName());
countDownLatch.countDown();
}, "tt-" + i).start());
countDownLatch.await();
System.out.println(count);
}
}
運行main()方法總是打印出10000000(一千萬),說明這個鎖也是可以直接使用的,當然這也是一個不可重入的鎖。
是不是很簡單,只需要簡單地實現(xiàn)AQS的兩個方法就完成了上一章彤哥自己動手實現(xiàn)的鎖的功能。
它是怎么實現(xiàn)的呢?
我們這一章先不講源碼,后面學習了ReentrantLock自然就明白了。
這一章就到此結(jié)束了,本篇沒有去深入的解析AQS的源碼,筆者認為這沒有必要,因為對于從來都沒有看過鎖相關(guān)的源碼的同學來說,一上來就講AQS的源碼肯定會一臉懵逼的,具體的源碼我們穿插在后面的鎖和同步器的部分來學習,等所有跟AQS相關(guān)的源碼學習完畢了,再來一篇總結(jié)。
下面總結(jié)一下這一章的主要內(nèi)容:
(1)AQS是Java中幾乎所有鎖和同步器的一個基礎(chǔ)框架,這里說的是“幾乎”,因為有極個別確實沒有通過AQS來實現(xiàn);
(2)AQS中維護了一個隊列,這個隊列使用雙鏈表實現(xiàn),用于保存等待鎖排隊的線程;
(3)AQS中維護了一個狀態(tài)變量,控制這個狀態(tài)變量就可以實現(xiàn)加鎖解鎖操作了;
(4)基于AQS自己動手寫一個鎖非常簡單,只需要實現(xiàn)AQS的幾個方法即可。
上一章彤哥自己動手寫的鎖,其實可以看成是AQS的一個縮影,看懂了那個基本上AQS可以看懂一半了,因為彤哥那個里面沒有寫Condition相關(guān)的內(nèi)容,下一章ReentrantLock重入鎖中我們將一起學習Condition相關(guān)的內(nèi)容。
所以呢,還是建議大家去看看這篇文章,點擊下面的推薦閱讀可以直達。
死磕 java同步系列之自己動手寫一個鎖Lock
死磕 java魔法類之Unsafe解析
死磕 java同步系列之JMM(Java Memory Model)
死磕 java同步系列之volatile解析
歡迎關(guān)注我的公眾號“彤哥讀源碼”,查看更多源碼系列文章, 與彤哥一起暢游源碼的海洋。
創(chuàng)新互聯(lián)www.cdcxhl.cn,專業(yè)提供香港、美國云服務(wù)器,動態(tài)BGP最優(yōu)骨干路由自動選擇,持續(xù)穩(wěn)定高效的網(wǎng)絡(luò)助力業(yè)務(wù)部署。公司持有工信部辦法的idc、isp許可證, 機房獨有T級流量清洗系統(tǒng)配攻擊溯源,準確進行流量調(diào)度,確保服務(wù)器高可用性。佳節(jié)活動現(xiàn)已開啟,新人活動云服務(wù)器買多久送多久。