Lock接口
public interface Lock {
//加鎖
void lock();
//可中斷鎖與Lock不同之處在于可響應(yīng)中斷操作邀泉,即在獲取鎖的過程中可以中斷
void lockInterruptibly() throws InterruptedException;
//立即返回的獲取鎖,返回true則表示成功顽馋,false表示失敗
boolean tryLock();
//根據(jù)傳入時間立即返回的獲取鎖,返回true表示成功,false表示失敗
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
//釋放鎖
void unlock();
Condition newCondition();
}
Lock的實現(xiàn)
實現(xiàn)Lock接口的類有很多炕倘,以下為幾個常見的鎖實現(xiàn)
- ReentrantLock:表示重入鎖,它是唯一一個實現(xiàn)了Lock接口的類翰撑。重入鎖指的是線程在獲得鎖之后罩旋,再次獲取該鎖不需要阻塞,而是直接關(guān)聯(lián)一次計數(shù)器增加重入次數(shù)
- ReentrantReadWriteLock:重入讀寫鎖眶诈,它實現(xiàn)了ReadWriteLock接口涨醋,在這個類中維護了兩個鎖,一個是ReadLock逝撬,一個是WriteLock浴骂,他們都分別實現(xiàn)了Lock接口。讀寫鎖是一種適合讀多寫少的場景下解決線程安全問題的工具宪潮,基本原則是:讀和讀不互斥溯警、讀和寫互斥、寫和寫互斥狡相。 也就是說涉及到影響數(shù)據(jù)變化的操作都會存在互斥梯轻。
- StampedLock: stampedLock是JDK8引入的新的鎖機制,可以簡單認為是讀寫鎖的一個改進版本谣光,讀寫鎖雖然通過分離讀和寫的功能使得讀和讀之間可以完全并發(fā)檩淋,但是讀和寫是有沖突的,如果大量的讀線程存在萄金,可能會引起寫線程的饑餓蟀悦。stampedLock是一種樂觀的讀策略,使得讀線程完全不會阻塞寫線程氧敢。和ReadWriteLock相比日戈,寫入的加鎖是完全一樣的,不同的是讀取孙乖。注意到首先我們通過tryOptimisticRead()獲取一個樂觀讀鎖浙炼,并返回版本號。接著進行讀取唯袄,讀取完成后弯屈,我們通過validate()去驗證版本號,如果在讀取過程中沒有寫入恋拷,版本號不變资厉,驗證成功,我們就可以放心地繼續(xù)后續(xù)操作蔬顾。如果在讀取過程中有寫入宴偿,版本號會發(fā)生變化湘捎,驗證將失敗。在失敗的時候窄刘,我們再通過獲取悲觀讀鎖再次讀取窥妇。由于寫入的概率不高,程序在絕大部分情況下可以通過樂觀讀鎖獲取數(shù)據(jù)娩践,極少數(shù)情況下使用悲觀讀鎖獲取數(shù)據(jù)
另外是不可重入鎖
活翩,不能在一個線程中反復(fù)獲取同一個鎖。
我們一般使用ReentrantLock:
Lock lock=new ReentrantLock()
lock.lock();
try{
//代碼....
}finally{
lock.unlock();
}
ReentrantLock默認是非公平鎖欺矫,我們先來了解下
公平鎖和非公平鎖特點
公平鎖是指多個線程按照申請鎖的順序來獲取鎖纱新,線程直接進入FIFO隊列,隊列中的第一個線程才能獲得鎖穆趴。公平鎖的優(yōu)點是等待鎖的線程不會夯死脸爱。缺點是吞吐效率相對非公平鎖要低,等待隊列中除了第一個線程以外的所有線程都會阻塞未妹,CPU喚醒阻塞線程的開銷比非公平鎖大簿废。
非公平鎖是多個線程加鎖時直接嘗試獲取鎖,獲取不到才會到等待隊列的隊尾等待络它。但是如果此時鎖剛好可用族檬,那么該線程可以無需阻塞直接獲取到鎖,所以非公平鎖有可能出現(xiàn)后申請鎖的線程先獲取到鎖的場景化戳。 非公平鎖的優(yōu)點就是可以減少喚起線程的開銷(因為可能有的線程可以直接獲取到鎖单料,CPU也就不用喚醒它),所以整體的吞吐效率高。缺點是處于等待隊列中的線程可能會夯死(如果每次有線程來点楼,它恰巧每次都獲取到鎖扫尖,此時還在排隊等待鎖的線程就悲劇了),或者等很久才會獲得鎖掠廓。
ReentrantLock與AQS
public class ReentrantLock implements Lock, java.io.Serializable {
private final Sync sync;
/**
* Base of synchronization control for this lock. Subclassed
* into fair and nonfair versions below. Uses AQS state to
* represent the number of holds on the lock.
*/
abstract static class Sync extends AbstractQueuedSynchronizer {
........
}
/**
* Sync object for non-fair locks
*/
static final class NonfairSync extends Sync {
.....
}
/**
* Sync object for fair locks
*/
static final class FairSync extends Sync {
....
}
}
- ReentrantLock實現(xiàn)了Lock接口换怖,內(nèi)部類有Sync,NonfairSync,FairSync(他們?nèi)齻€繼承了AQS),創(chuàng)建ReentrantLock時可以指定是非公平鎖還是公平鎖
- Sync:是抽象類蟀瞧,實現(xiàn)了tryRelease方法沉颂,tryAccquire方法由它的子類NonfairSync,FairSync自己實現(xiàn)。
- AQS:是一個抽象類悦污,但是代碼中卻沒有一個抽象方法铸屉,其中獲取鎖(tryAcquire方法)和釋放鎖(tryRelease方法)并沒有提供默認實現(xiàn),需要子類重寫這個兩個方法實現(xiàn)具體邏輯
- Node:AQS的內(nèi)部類切端,本質(zhì)上是一個雙向鏈表抬探,用來管理獲取鎖的線程。
Node和AQS工作原理
AQS是提供基礎(chǔ)設(shè)施帆赢,如構(gòu)建同步隊列小压,控制同步狀態(tài)等,那么它是如何構(gòu)建同步隊列的呢椰于?它的工作原理是怎么樣的呢怠益?
public abstract class AbstractQueuedSynchronizer
extends AbstractOwnableSynchronizer
implements java.io.Serializable {
//指向同步隊列對頭
private transient volatile Node head;
//指向同步隊列的隊尾
private transient volatile Node tail;
//同步狀態(tài),0代表鎖未被占用,1代表鎖已經(jīng)占用
/**
* The synchronization state.
*/
private volatile int state;
.......
}
再看看Node這個內(nèi)部類:它是對每一個訪問同步代碼塊的線程的封裝瘾婿,關(guān)于等待狀態(tài)蜻牢,我們暫時只需要關(guān)注SINAL和初始化狀態(tài)即可
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;
static final int CONDITION = -2;
static final int PROPAGATE = -3;
volatile int waitStatus;
volatile Node prev; //前驅(qū)節(jié)點
volatile Node next; //后繼節(jié)點
volatile Thread thread;//當(dāng)前線程
Node nextWaiter; //存儲在condition隊列中的后繼節(jié)點
//是否為共享鎖
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
}
//將線程構(gòu)造成一個Node,添加到等待隊列
Node(Node nextWaiter) { // Used by addWaiter
this.nextWaiter = nextWaiter;
U.putObject(this, THREAD, Thread.currentThread());
}
//這個方法會在Condition隊列使用偏陪,
Node(int waitStatus) { // Used by Condition
U.putInt(this, WAITSTATUS, waitStatus);
U.putObject(this, THREAD, Thread.currentThread());
}
}
- AQS本質(zhì)上就是由Node構(gòu)成的雙向鏈表抢呆,內(nèi)部有node head和node tail。
- AQS內(nèi)部通過state來控制同步狀態(tài)笛谦,當(dāng)state=0時抱虐,則說明沒有任何線程占有共享資源,當(dāng)state=1時饥脑,則說明有線程目前正在使用共享變量恳邀,其他線程必須加入同步隊列進行等待;
- AQS內(nèi)部通過內(nèi)部類Node構(gòu)成FIFO的同步隊列來完成線程獲取鎖的排隊工作灶轰,同時利用內(nèi)部類ConditionObject構(gòu)建等待隊列谣沸,當(dāng)Condtion調(diào)用wait()方法后,線程將會加入等待隊列中笋颤,而當(dāng)Condtion調(diào)用signal()方法后乳附,線程將從等待隊列轉(zhuǎn)移同步隊列中進行鎖競爭。注意這里涉及到兩種隊列伴澄,一種同步隊列赋除,當(dāng)線程請求鎖而等待,后將加入同步隊列秉版,另一種則是等待隊列贤重,通過Condition調(diào)用await()方法釋放鎖后,加入等待隊列清焕。
-
當(dāng)我們調(diào)用ReentrantLock.lock()方法時并蝗,實際操作的是基于Node結(jié)構(gòu)的同步隊列,此時AQS中的state變量則是代表同步狀態(tài)秸妥,加鎖后滚停,如果此時state的值為0,則說明當(dāng)前線程可以獲取到鎖粥惧,同時將state設(shè)置為1键畴,則表示獲取成功。如果調(diào)用ReentrantLock.lock()方法時sate已為1,也就是當(dāng)前鎖已被其他線程持有起惕,那么當(dāng)前執(zhí)行線程將被封裝為Node節(jié)點加入同步隊列等待.
image.png
獲取鎖
public ReentrantLock() {
sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
//加鎖操作
public void lock() {
sync.lock();
}
這里說明ReentrantLock默認就是構(gòu)造一個非公平鎖涡贱,調(diào)用lock方法時候:
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
/**
* Performs lock. Try immediate barge, backing up to normal
* acquire on failure.
*/
// Android-removed: @ReservedStackAccess from OpenJDK 9, not available on Android.
// @ReservedStackAccess
final void lock() {
執(zhí)行CAS操作,本質(zhì)就是CAS更新state惹想,判斷state是否為0问词,
如果為0則把0更新為1,并返回true否則返回false
if (compareAndSetState(0, 1))
//成功則將獨占鎖線程設(shè)置為當(dāng)前線程
setExclusiveOwnerThread(Thread.currentThread());
else
//否則再次請求同步狀態(tài)
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
也就是說,通過CAS機制保證并發(fā)的情況下只有一個線程可以成功將state設(shè)置為1嘀粱,獲取到鎖激挪;此時,其他線程在執(zhí)行compareAndSetState時锋叨,因為state此時不是0垄分,所以會失敗并返回false,執(zhí)行acquire(1);
加入同步隊列:
public final void acquire(int arg) {
//再次嘗試獲取同步狀態(tài)
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
/**
* Performs non-fair tryLock. tryAcquire is implemented in
* subclasses, but both need nonfair try for trylock method.
*/
// Android-removed: @ReservedStackAccess from OpenJDK 9, not available on Android.
// @ReservedStackAccess
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState(); //獲取volatile類型的state
if (c == 0) {
//這里就是非公平鎖的訣竅:state為0表示當(dāng)前沒有線程獲取鎖,
//此時不管條件隊列的順序娃磺,只要有線程獲取鎖失敗則會執(zhí)行CAS操作再次嘗試獲取一次.
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
//這是表示獲取鎖的線程是Owner,也就是重入鎖線程加鎖的情況薄湿,
//這時status會再次+1,因為是同一個線程所以不會有線程安全問題
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
假設(shè)有三個線程:線程1已經(jīng)獲得了鎖豌鸡,線程2正在同步隊列中排隊嘿般,此時線程3執(zhí)行l(wèi)ock方法嘗試獲取鎖的時候,線程1正好釋放了鎖涯冠,將state更新為0炉奴,那么線程3就可能在線程2還沒有被喚醒之前去獲取到這個鎖。
如果此時還沒獲取到鎖蛇更,那么接下來就會把該線程封裝成node去同步隊列里排隊瞻赶,acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
/**
* Creates and enqueues node for current thread and given mode.
*
* @param mode Node.EXCLUSIVE for exclusive, Node.SHARED for shared
* @return the new node
*/
private Node addWaiter(Node mode) {
Node node = new Node(mode);
//死循環(huán)
for (;;) {
Node oldTail = tail;
if (oldTail != null) {
U.putObject(node, Node.PREV, oldTail);
//隊尾添加新結(jié)點
if (compareAndSetTail(oldTail, node)) {
oldTail.next = node;
return node;
}
} else {
initializeSyncQueue();
}
}
}
private final void initializeSyncQueue() {
Node h;
if (U.compareAndSwapObject(this, HEAD, null, (h = new Node())))
tail = h;
}
這個方法使用一個死循環(huán)進行CAS操作,可以解決多線程并發(fā)問題派任。假設(shè)線程1,2,3砸逊,4同時執(zhí)行addWaiter()方法入隊,而此時頭節(jié)點不為null掌逛,那么他們會同時執(zhí)行addWaiter中的compareAndSetTail方法將隊尾指向它师逸,添加到隊尾。但是這個時候CAS操作保證只有一個可以成功豆混,假設(shè)此時線程1成功添加到隊尾篓像,那么線程2,3,4執(zhí)行CAS都會失敗,方法內(nèi)部死循環(huán)保證所有線程直到成功添加到隊尾為止.
這里會涉及到兩個變化
- 新的線程封裝成Node節(jié)點追加到同步隊列中皿伺,設(shè)置prev節(jié)點以及修改當(dāng)前節(jié)點的前置節(jié)點的next節(jié)點指向自己
- 通過CAS講tail重新指向新的尾部節(jié)點
自旋
添加到同步隊列后员辩,結(jié)點就會進入一個自旋過程,即每個結(jié)點都在觀察時機待條件滿足獲取同步狀態(tài)鸵鸥,自旋過程是在acquireQueued方法中執(zhí)行的.
final boolean acquireQueued(final Node node, int arg) {
try {
boolean interrupted = false;
//死循環(huán) 自旋
for (;;) {
//獲取前置節(jié)點
final Node p = node.predecessor();
當(dāng)且僅當(dāng)p為頭結(jié)點才嘗試獲取同步狀態(tài)奠滑,此時符合FIFO的原則
if (p == head && tryAcquire(arg)) {
//將Node設(shè)置為頭結(jié)點
setHead(node);
//清空原頭結(jié)點的引用便于GC
p.next = null; // help GC
return interrupted;
}
//如果前驅(qū)結(jié)點不是head,判斷是否掛起線程
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} catch (Throwable t) {
//最終都沒能獲取到同步狀態(tài) 結(jié)束該線程的請求
cancelAcquire(node);
throw t;
}
}
//設(shè)置頭結(jié)點
private void setHead(Node node) {
head = node;
清空結(jié)點數(shù)據(jù) 以便于GC
node.thread = null;
node.prev = null;
}
這個過程也是涉及到兩個變化
- 修改head節(jié)點指向下一個獲得鎖的節(jié)點
- 新的獲得鎖的節(jié)點,將prev的指針指向null
這里有一個小的變化宋税,就是設(shè)置head節(jié)點不需要用CAS摊崭,原因是設(shè)置head節(jié)點是由獲得鎖的線程來完成的,而同步鎖只能由一個線程獲得弃甥,所以不需要CAS保證爽室,只需要把head節(jié)點設(shè)置為原首節(jié)點的后繼節(jié)點,并且斷開原h(huán)ead節(jié)點的next引用即可
當(dāng)然如果前驅(qū)結(jié)點不是head而它又沒有獲取到鎖淆攻,那么執(zhí)行如下:
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
//獲取當(dāng)前結(jié)點的等待狀態(tài)
int ws = pred.waitStatus;
//如果是等待喚醒 狀態(tài) 則返回true
if (ws == Node.SIGNAL)
/*
* This node has already set status asking a release
* to signal it, so it can safely park.
*/
return true;
//如果ws>0 說明是結(jié)束狀態(tài)
//遍歷前驅(qū)結(jié)點直到找到?jīng)]有結(jié)束狀態(tài)的節(jié)點
if (ws > 0) {
/*
* Predecessor was cancelled. Skip over predecessors and
* indicate retry.
*/
//如果前節(jié)點被取消,則將取消節(jié)點移除隊列操作
//找到當(dāng)前節(jié)點的前節(jié)點嘿架,如果前節(jié)點為取消節(jié)點則一直往前尋找一個節(jié)點瓶珊。pred.waitStatus > 0標(biāo)示已取消
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
//如果ws小于0又不是SIGNAL狀態(tài)
//則將其設(shè)置為SIGNAL狀態(tài) 代表該節(jié)點的線程正在等待喚醒
/*
* 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.
*/
pred.compareAndSetWaitStatus(ws, Node.SIGNAL);
}
return false;
}
private final boolean parkAndCheckInterrupt() {
//將當(dāng)前線程掛起 線程會阻塞住
LockSupport.park(this);
//獲取線程中斷狀態(tài),interrupted是判斷當(dāng)前中斷狀態(tài)
//并非中斷線程 因此可能返回true也可能返回false
return Thread.interrupted();
}
這段代碼有個設(shè)計比較好的點:
通常我們在設(shè)計隊列時,我們需要考慮如何最大化的減少后續(xù)排隊節(jié)點對于CPU的消耗耸彪,而在AQS中伞芹,只要當(dāng)前節(jié)點的前驅(qū)結(jié)點不是頭節(jié)點,再把當(dāng)前節(jié)點加入到隊列后就會執(zhí)行LockSupport.park(this)蝉娜;將當(dāng)前線程掛起唱较,這樣可以最大程度減少CPU消耗.
AQS通過最簡單的CAS和LockSupport的park,設(shè)計出了高效的隊列模型和機制:
1.AQS結(jié)構(gòu)其實是在第二個線程獲取鎖的時候在初始化召川,就是lazy-init的思想南缓,最大限度減少不必要的代碼執(zhí)行的開銷
2.為了最大程度上提升效率,盡量避免線程間的通訊荧呐,采用了雙向鏈表的Node結(jié)構(gòu)去存儲線程
3.為了最大程度上避免CPU上下文切換執(zhí)行的消耗汉形,在設(shè)計排隊線程時,只有頭結(jié)點的下一個的線程一直在重復(fù)執(zhí)行后去鎖倍阐,隊列后面的線程會通過LockSupport進行休眠概疆。
非公平鎖的釋放
//ReentrantLock類的unlock方法
public void unlock() {
sync.release(1);
}
//AQS的release方法
public final boolean release(int arg) {
//嘗試釋放鎖
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
//喚醒后繼結(jié)點的線程
unparkSuccessor(h);
return true;
}
return false;
}
//ReentrantLock類中的內(nèi)部類Sync實現(xiàn)的tryRelease(int releases)方法
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
//判斷狀態(tài)是否為0岔冀,如果是則說明已釋放同步狀態(tài)
if (c == 0) {
free = true;
//設(shè)置Owner為null
setExclusiveOwnerThread(null);
}
//設(shè)置更新同步狀態(tài)
setState(c);
return free;
}
unParkSucessor(h)的作用喚醒后續(xù)的節(jié)點:
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)
node.compareAndSetWaitStatus(ws, 0);
/*
* Thread to unpark is held in successor, which is normally
* just the next node. But if cancelled or apparently null,
* traverse backwards from tail to find the actual
* non-cancelled successor.
*/
Node s = node.next;
if (s == null || s.waitStatus > 0) {
s = null;
for (Node p = tail; p != node && p != null; p = p.prev)
if (p.waitStatus <= 0)
s = p;
}
if (s != null)
LockSupport.unpark(s.thread); //喚醒
}
ReentrantLock中的公平鎖
與非公平鎖不同的是春锋,在獲取鎖的時候侧馅,公平鎖的獲取順序是完全遵循FIFO規(guī)則谊娇,也就是說先請求的線程一定會先獲取鎖小渊,后來的線程肯定需要排隊.
下面比較一下公平鎖和非公平鎖lock方法:
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
/**
* Performs lock. Try immediate barge, backing up to normal
* acquire on failure.
*/
// Android-removed: @ReservedStackAccess from OpenJDK 9, not available on Android.
// @ReservedStackAccess
//非公平鎖:一上來二話不說直接CAS設(shè)置state搶鎖
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
公平鎖的lock方法:
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
final void lock() {
acquire(1);
}
/**
* Fair version of tryAcquire. Don't grant access unless
* recursive call or no waiters or is first.
*/
// Android-removed: @ReservedStackAccess from OpenJDK 9, not available on Android.
// @ReservedStackAccess
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
//先判斷同步隊列是否存在節(jié)點
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
}
public final boolean hasQueuedPredecessors() {
// The correctness of this depends on head being initialized
// before tail and on head.next being accurate if the current
// thread is first in queue.
Node t = tail; // Read fields in reverse initialization order
Node h = head;
Node s;
return h != t &&
((s = h.next) == null || s.thread != Thread.currentThread());
}
唯一的差別就是hasQueuedPredecessors()判斷同步隊列是否存在結(jié)點谬擦,這就是非公平鎖與公平鎖最大的區(qū)別,即公平鎖在線程請求到來時先會判斷同步隊列是否存在結(jié)點,如果存在先執(zhí)行同步隊列中的結(jié)點線程,當(dāng)前線程將封裝成node加入同步隊列等待虏冻。而非公平鎖呢鸥鹉,當(dāng)線程請求到來時灸异,不管同步隊列是否存在線程結(jié)點,直接嘗試獲取同步狀態(tài)褥傍,獲取成功直接訪問共享資源儡嘶,但請注意在絕大多數(shù)情況下,非公平鎖才是我們理想的選擇恍风,畢竟從效率上來說非公平鎖總是勝于公平鎖。
總結(jié):
以上便是ReentrantLock的內(nèi)部實現(xiàn)原理誓篱,這里我們簡單進行小結(jié)朋贬,重入鎖ReentrantLock,是一個基于AQS并發(fā)框架的并發(fā)控制類窜骄,其內(nèi)部實現(xiàn)了3個類锦募,分別是Sync、NoFairSync以及FairSync類邻遏,其中Sync繼承自AQS糠亩,實現(xiàn)了釋放鎖的模板方法tryRelease(int),而NoFairSync和FairSync都繼承自Sync准验,實現(xiàn)各種獲取鎖的方法tryAcquire(int)
AQS在設(shè)計時將性能優(yōu)化到了極致赎线,具體體現(xiàn)在同步隊列的park和unpark,初始化AQS時的懶加載糊饱,以及線程之間通過Node這樣的數(shù)據(jù)結(jié)構(gòu)從而避免線程間通訊造成的額外開銷垂寥,這種由釋放鎖的線程主動喚醒后續(xù)線程的方式也是我們再實際過程中可以借鑒的.