從零到一實(shí)現(xiàn)互斥鎖,理清AQS和Lock的關(guān)系。
作者:Mulavar
來(lái)源:SegmentFault社區(qū)
1. AQS原理
1.1 AQS簡(jiǎn)介
AQS全名AbstractQueuedSynchronizer,是一個(gè)隊(duì)列同步器,JUC的鎖和同步組件都是基于AQS構(gòu)建的。AQS的結(jié)構(gòu)主要分兩部分:
state:用了violate關(guān)鍵字修飾的int類型成員變量,表示同步狀態(tài),每次更新都能被所有線程可見(jiàn); head、tail:內(nèi)置的Node類型(Node包含當(dāng)前線程、等待狀態(tài)等信息),組成了一個(gè)FIFO的同步隊(duì)列。
AQS的主要使用方式是繼承,它采用了模板方法的設(shè)計(jì)模式。什么是模板方法設(shè)計(jì)模式?簡(jiǎn)單來(lái)說(shuō),就是一個(gè)類將一些不變行為封裝成final方法(即模板方法),將剩余的可變方法(abstract)留給子類實(shí)現(xiàn)去拓展。
1.2 AQS接口
AQS主要提供了如下三個(gè)方法來(lái)訪問(wèn)或修改同步狀態(tài)state:
getState():獲取當(dāng)前同步狀態(tài) setState(int newState):設(shè)置當(dāng)前同步狀態(tài) compareAndSetState(int expect, int update):使用CAS設(shè)置當(dāng)前狀態(tài),該方法能夠保證狀態(tài)設(shè)置的原子性。
1.2.1 AQS模板方法

下面列舉幾個(gè)最為核心的模板方法:
1.2.2 AQS重寫接口
而AQS可重寫的方法如下:

可以看到,AQS利用模板方法主要做了兩件事:
嘗試獲取或設(shè)置同步狀態(tài) 維護(hù)同步隊(duì)列
而留給開(kāi)發(fā)者的可重寫方法更多聚焦于如何設(shè)置、獲取同步狀態(tài),這種設(shè)計(jì)思想與雙親委派機(jī)制有點(diǎn)相似:loadClass中實(shí)現(xiàn)了雙親委派機(jī)制,而用戶可以通過(guò)重寫findClass去實(shí)現(xiàn)自己加載類的具體邏輯。
2. Lock和AQS的關(guān)系
AQS是實(shí)現(xiàn)Lock的基礎(chǔ),兩者之間的主要區(qū)別在于:
Lock面向鎖的使用者,它聚焦的問(wèn)題是使用者如何更好地使用鎖處理并發(fā)問(wèn)題,而使用者不需要知道鎖的實(shí)現(xiàn)細(xì)節(jié)就可以實(shí)現(xiàn)互斥同步; AQS面向鎖的開(kāi)發(fā)者,它關(guān)注的兩個(gè)主要問(wèn)題是同步狀態(tài)管理以及維護(hù)線程的同步等待隊(duì)列。
3. 自定義實(shí)現(xiàn)獨(dú)占鎖
該節(jié)通過(guò)自定義實(shí)現(xiàn)獨(dú)占鎖Demo,去更加清晰揭示AQS和Lock接口的區(qū)別。
首先,我們定義了一個(gè)獨(dú)占鎖—Mutex實(shí)現(xiàn)Lock接口,并實(shí)現(xiàn)該接口的所有方法。而Lock接口的方法實(shí)現(xiàn)中所做的是都是調(diào)用AQS的模板方法。
其次,我們創(chuàng)建了一個(gè)內(nèi)部類—Sync,實(shí)現(xiàn)了一個(gè)自定義同步器,并重寫了其中的所有非模板方法,主要思想是通過(guò)getState()、setState(int newState)和compareAndSetState(int expect, int update)三個(gè)方法去設(shè)置和獲取同步狀態(tài),具體實(shí)現(xiàn)可看代碼注釋。
至此我們總結(jié)一下其中的一個(gè)核心調(diào)用鏈lock --> acquire() --> tryAcquire():
lock:面向用戶提供鎖功能 acquire:嘗試設(shè)置同步狀態(tài)并維護(hù)同步隊(duì)列 tryAquire:設(shè)置同步狀態(tài)的真正邏輯
import java.util.concurrent.TimeUnit;import java.util.concurrent.locks.AbstractQueuedSynchronizer;import java.util.concurrent.locks.Condition;import java.util.concurrent.locks.Lock;/*** @author Lam* @date 2020/7/27*/public class Mutex implements Lock {private final Sync sync = new Sync();/*** 同步器實(shí)現(xiàn),重寫非模板方法* 主要思想是使用CAS設(shè)置state的狀態(tài)*/private static class Sync extends AbstractQueuedSynchronizer {protected boolean tryAcquire(int arg) {// CAS設(shè)置stateif (compareAndSetState(0, 1)) {// 設(shè)置該線程為成功獲取同步狀態(tài)的線程setExclusiveOwnerThread(Thread.currentThread());return true;}// 設(shè)置狀態(tài)失敗return false;}protected boolean tryRelease(int arg) {// 若此時(shí)同步狀態(tài)為0說(shuō)明沒(méi)有線程設(shè)置過(guò)同步狀態(tài)if (getState()==0) {throw new IllegalMonitorStateException();}// 清空成功獲取同步狀態(tài)的線程信息setExclusiveOwnerThread(null);// 清除狀態(tài)setState(0);return true;}protected boolean isHeldExclusively() {return getState() == 1;}/*** 該方法不是AQS的重寫方法,只是為了代碼風(fēng)格統(tǒng)一將邏輯都放到Sync中*/Condition newCondition() {// 返回一個(gè)等待通知組件return new ConditionObject();}}/*** Lock接口的實(shí)現(xiàn)*/public void lock() {sync.acquire(1);}public void lockInterruptibly() throws InterruptedException {sync.acquireInterruptibly(1);}public boolean tryLock() {return sync.tryAcquire(1);}public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {return sync.tryAcquireNanos(1, unit.toNanos(time));}public void unlock() {sync.release(1);}public Condition newCondition() {return sync.newCondition();}}
4. 總結(jié)
本文大部分內(nèi)容取自Java并發(fā)編程的藝術(shù),在一些小地方做了刪改并加了自己的理解,如果想更完全深入了解AQS的模板方法和非模板方法有哪些可以查看原書第五章的1、2節(jié)。

