AQS

AbstractQueuedSynchronizer源碼

AbstractQueuedSynchronizer(AQS)內(nèi)部結(jié)構(gòu)

AQS是ReentrantLock、ReentrantReadWriteLock斑唬、CountDownLatch等經(jīng)常使用鎖的父類(lèi)撩炊。AQS既然是父類(lèi)砂竖,一些接口是需要實(shí)現(xiàn)類(lèi)自己去實(shí)現(xiàn)的例如tryAcquire么介,tryRelase罪治;AQS提供共享鎖和排它鎖(獨(dú)占鎖)。

AQS內(nèi)部有個(gè)Node礁蔗,申請(qǐng)鎖的線(xiàn)程會(huì)封裝為一個(gè)Node觉义,申請(qǐng)一個(gè)鎖的所有線(xiàn)程會(huì)組成Node鏈表。

  1. 鏈表是雙鏈表結(jié)構(gòu)

  2. 非租塞浴井,在并發(fā)下插入和移除操作不會(huì)阻塞(自旋晒骇,CAS)

  3. FIFO

AQS內(nèi)還維護(hù)了state(內(nèi)部同步狀態(tài))、線(xiàn)程的阻塞和解除操作滋饲。

所有對(duì)state操作是原子性的

AQS內(nèi)部使用LockSuppert.park厉碟、unPark操作。內(nèi)部會(huì)在每個(gè)Thread設(shè)置一個(gè)本地狀態(tài)屠缭,讓線(xiàn)程輪訓(xùn)本地狀態(tài)箍鼓,而不是像普通的鎖,大家都是競(jìng)爭(zhēng)同一個(gè)鎖呵曹,造成更大的浪費(fèi)款咖。

Node的雙鏈表結(jié)構(gòu)圖

Node結(jié)構(gòu).png

Node的屬性:

1.prev 指向節(jié)點(diǎn)上一個(gè)節(jié)點(diǎn),如果沒(méi)有則為null

2.next 指向節(jié)點(diǎn)下一個(gè)節(jié)點(diǎn)奄喂,為了查找最后一個(gè)節(jié)點(diǎn)方便铐殃,避免單鏈表情況下必須從head開(kāi)始找,如果沒(méi)有則為null

3.waitStatus 線(xiàn)程狀態(tài),CLH結(jié)構(gòu)中使用前一節(jié)點(diǎn)一個(gè)屬性標(biāo)識(shí)當(dāng)前節(jié)點(diǎn)狀態(tài)跨新,方便實(shí)現(xiàn)取消和超時(shí)功能富腊。

? CANCELLED =1,被取消或超時(shí)

? SIGNAL =-1 域帐,只是當(dāng)前節(jié)點(diǎn)的后續(xù)節(jié)點(diǎn)被阻塞了赘被,需要unparking

? CONDITION = -2,當(dāng)前節(jié)點(diǎn)在conditon隊(duì)列中

? PROPAGATE = -3肖揣,共享鎖使用民假,線(xiàn)性廣播喚醒線(xiàn)程。

默認(rèn) 0龙优,初始狀態(tài)

4.nextWaiter:標(biāo)識(shí)condtion隊(duì)列的后續(xù)節(jié)點(diǎn)羊异,此時(shí)prev,next不用彤断,而且節(jié)點(diǎn)waitStauts是CONDITION.

5.thread 對(duì)應(yīng)的線(xiàn)程

源代碼排它鎖(ReentrantLock)

ReentrantLock使用方式 1):

ReentrantLock.lock

ReentrantLock.unlock

aqs流程

ReentrantLock lock過(guò)程

ReentrantLock內(nèi)部有公平鎖FairSync和非公平鎖NonfairSync野舶,先看公平鎖

ReentrantLock:

 public void lock() {
 sync.lock();
 }
//AbstractQueuedSynchronizer:
 public final void acquire(int arg) {
 if (!tryAcquire(arg) &&
 acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) //如果沒(méi)有獲取到鎖 就創(chuàng)建Node節(jié)點(diǎn) 封裝當(dāng)前線(xiàn)程,成功后并中斷當(dāng)前線(xiàn)程
 selfInterrupt();
 }

private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
 int ws = pred.waitStatus;
 if (ws == Node.SIGNAL) // 需要unpark
 /*
 * This node has already set status asking a release
 * to signal it, so it can safely park.
 */
 return true;
 if (ws > 0) {
 /*
 * Predecessor was cancelled. Skip over predecessors and
 * indicate retry.
 */
 do {
 node.prev = pred = pred.prev;
 } while (pred.waitStatus > 0); //大于1的都是取消的宰衙,要找到小與0的
 pred.next = node;
 } else {
 /*
 * 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.
 */
 compareAndSetWaitStatus(pred, ws, Node.SIGNAL);  // 需要前驅(qū)節(jié)點(diǎn)(waitStatus)unpark筒愚,也就說(shuō)告訴前節(jié)點(diǎn),你的next節(jié)點(diǎn)需要被通知運(yùn)行
 }
 return false;
 }
 final boolean acquireQueued(final Node node, int arg) {
 boolean failed = true;
 try {
 boolean interrupted = false;
 for (;;) {
 final Node p = node.predecessor(); //取出前節(jié)點(diǎn)
 if (p == head && tryAcquire(arg)) {//如果前節(jié)點(diǎn)是head,則設(shè)置改節(jié)點(diǎn)為頭結(jié)點(diǎn)
 setHead(node);
 p.next = null; // help GC
 failed = false;
 return interrupted; //并不中斷
 }
 if (shouldParkAfterFailedAcquire(p, node) &&
 parkAndCheckInterrupt()) //這里會(huì)有LockSupport操作,
 //LockSupport.park unPark,這里會(huì)有一個(gè)許可纹安。park在獲取不到許可就會(huì)組阻塞陆淀,
 interrupted = true;
 }
 } finally {
 if (failed)  //失敗就會(huì)取消線(xiàn)程 
 cancelAcquire(node); //取消后還要調(diào)整隊(duì)列
 }
 }
/**
 * 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(Thread.currentThread(), mode); //新建節(jié)點(diǎn) 并入隊(duì)尾 參數(shù)是模式
 // Try the fast path of enq; backup to full enq on failure
 Node pred = tail;
 if (pred != null) {
 node.prev = pred;
 if (compareAndSetTail(pred, node)) {  //如果有并發(fā)考余,這里會(huì)失敗,進(jìn)入enq
 pred.next = node;
 return node;
 }
 }
 enq(node);
 return node;
 }
 /**
 * Inserts node into queue, initializing if necessary. See picture above.
 * @param node the node to insert
 * @return node's predecessor
 */
 private Node enq(final Node node) {
 for (;;) {    //CAS 操作轧苫,自旋模式 直到設(shè)置成功哦
 Node t = tail;
 if (t == null) { // Must initialize
 if (compareAndSetHead(new Node()))
 tail = head;
 } else {
 node.prev = t;
 if (compareAndSetTail(t, node)) {
 t.next = node;
 return t;
 }
 }
 }
 }
//FairSync:
 final void lock() {
 acquire(1); //基類(lèi)AQS方法
 }
 /**
 * Fair version of tryAcquire.  Don't grant access unless
 * recursive call or no waiters or is first.
 */
 protected final boolean tryAcquire(int acquires) {
 final Thread current = Thread.currentThread();
 int c = getState();
 if (c == 0) { //這里是判斷是否是空鏈表或者當(dāng)前線(xiàn)程是鏈表頭部
 if (!hasQueuedPredecessors() && //判斷是否有pre節(jié)點(diǎn) 不包括head
 compareAndSetState(0, acquires)) {
 setExclusiveOwnerThread(current);//設(shè)置為當(dāng)前線(xiàn)程
 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; //沒(méi)有獲取到返回false
 }

ReentrantLock unlock過(guò)程

//ReentrantLock:
public void unlock() {
 sync.release(1);
 }
//AQS:
public final boolean release(int arg) {
 if (tryRelease(arg)) {
 Node h = head;
 if (h != null && h.waitStatus != 0) //如果不是head不為空楚堤,切不為0,則要喚醒后續(xù)節(jié)點(diǎn)
 unparkSuccessor(h);
 return true;
 }
 return false;
 }
//sync:
 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)
 compareAndSetWaitStatus(node, 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) {  //找到小于0的可執(zhí)行的
 s = null;
 for (Node t = tail; t != null && t != node; t = t.prev) 
 if (t.waitStatus <= 0)
 s = t;
 }
 if (s != null)
 LockSupport.unpark(s.thread); //釋放
 }
?
 protected final boolean tryRelease(int releases) {
 int c = getState() - releases; //同步參數(shù) -1
 if (Thread.currentThread() != getExclusiveOwnerThread()) //不等于當(dāng)前線(xiàn)程則會(huì)有錯(cuò)誤
 throw new IllegalMonitorStateException();
 boolean free = false;
 if (c == 0) { //如果沒(méi)有(并發(fā))含懊,則設(shè)置為null身冬,
 free = true;
 setExclusiveOwnerThread(null); 
 }
 setState(c);//應(yīng)該和重入哦 重入鎖解鎖的時(shí)候不會(huì)喚醒下一個(gè)線(xiàn)程
 return free;
 }

非公平鎖:

非公平鎖很簡(jiǎn)單,在開(kāi)始是直接去爭(zhēng)奪修改status(0->1)岔乔,沒(méi)有使用鏈表酥筝,誰(shuí)修改status成功誰(shuí)拿到鎖

 final void lock() {
 if (compareAndSetState(0, 1)) //
 setExclusiveOwnerThread(Thread.currentThread());
 else
 acquire(1);
 }
?
 protected final boolean tryAcquire(int acquires) {
 return nonfairTryAcquire(acquires);
 }
 final boolean nonfairTryAcquire(int acquires) {
 final Thread current = Thread.currentThread();
 int c = getState();
 if (c == 0) {
 if (compareAndSetState(0, acquires)) { //
 setExclusiveOwnerThread(current);
 return true;
 }
 }
 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;
 }

ReentrantLock使用方式 2):

Condtion condtion = ReentrantLock.newConditon();

condtion.await 和Object類(lèi)的wait方法等效

condtion.signal 和Object類(lèi)的notify方法等

condtion.signalAll Object類(lèi)的notifyAll方法等效

ReentrantLock condition實(shí)現(xiàn)類(lèi) ConditionObject

Node 鏈表只使用了nextWaiter,組成一個(gè)隊(duì)列,調(diào)用await會(huì)加入node鏈表tail雏门,signal是通知head節(jié)點(diǎn)運(yùn)行嘿歌;signalALl是遍歷鏈表,都去競(jìng)爭(zhēng)啊

//共享鎖 wait

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末茁影,一起剝皮案震驚了整個(gè)濱河市宙帝,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌募闲,老刑警劉巖步脓,帶你破解...
    沈念sama閱讀 219,427評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異浩螺,居然都是意外死亡沪编,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,551評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)年扩,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人访圃,你說(shuō)我怎么就攤上這事厨幻。” “怎么了腿时?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,747評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵况脆,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我批糟,道長(zhǎng)格了,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,939評(píng)論 1 295
  • 正文 為了忘掉前任徽鼎,我火速辦了婚禮盛末,結(jié)果婚禮上弹惦,老公的妹妹穿的比我還像新娘。我一直安慰自己悄但,他們只是感情好棠隐,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,955評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著檐嚣,像睡著了一般助泽。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上嚎京,一...
    開(kāi)封第一講書(shū)人閱讀 51,737評(píng)論 1 305
  • 那天嗡贺,我揣著相機(jī)與錄音,去河邊找鬼鞍帝。 笑死诫睬,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的膜眠。 我是一名探鬼主播岩臣,決...
    沈念sama閱讀 40,448評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼宵膨!你這毒婦竟也來(lái)了架谎?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,352評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤辟躏,失蹤者是張志新(化名)和其女友劉穎谷扣,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體捎琐,經(jīng)...
    沈念sama閱讀 45,834評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡会涎,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,992評(píng)論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了瑞凑。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片末秃。...
    茶點(diǎn)故事閱讀 40,133評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖籽御,靈堂內(nèi)的尸體忽然破棺而出练慕,到底是詐尸還是另有隱情,我是刑警寧澤技掏,帶...
    沈念sama閱讀 35,815評(píng)論 5 346
  • 正文 年R本政府宣布铃将,位于F島的核電站,受9級(jí)特大地震影響哑梳,放射性物質(zhì)發(fā)生泄漏劲阎。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,477評(píng)論 3 331
  • 文/蒙蒙 一鸠真、第九天 我趴在偏房一處隱蔽的房頂上張望悯仙。 院中可真熱鬧龄毡,春花似錦、人聲如沸雁比。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,022評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)偎捎。三九已至蠢终,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間茴她,已是汗流浹背寻拂。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,147評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留丈牢,地道東北人祭钉。 一個(gè)月前我還...
    沈念sama閱讀 48,398評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像己沛,于是被迫代替她去往敵國(guó)和親慌核。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,077評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容