死磕 java同步系列之AQS終篇(面試)

問題

(1)AQS的定位?java

(2)AQS的重要組成部分?面試

(3)AQS運用的設計模式?設計模式

(4)AQS的整體流程?框架

簡介

AQS的全稱是AbstractQueuedSynchronizer,它的定位是爲Java中幾乎全部的鎖和同步器提供一個基礎框架。學習

在以前的章節中,咱們一塊兒學習了ReentrantLock、ReentrantReadWriteLock、Semaphore、CountDownLatch的源碼,今天咱們一塊兒來對AQS作個總結。ui

狀態變量state

AQS中定義了一個狀態變量state,它有如下兩種使用方法:線程

(1)互斥鎖設計

當AQS只實現爲互斥鎖的時候,每次只要原子更新state的值從0變爲1成功了就獲取了鎖,可重入是經過不斷把state原子更新加1實現的。code

(2)互斥鎖 + 共享鎖接口

當AQS須要同時實現爲互斥鎖+共享鎖的時候,低16位存儲互斥鎖的狀態,高16位存儲共享鎖的狀態,主要用於實現讀寫鎖。

互斥鎖是一種獨佔鎖,每次只容許一個線程獨佔,且當一個線程獨佔時,其它線程將沒法再獲取互斥鎖及共享鎖,可是它本身能夠獲取共享鎖。

共享鎖同時容許多個線程佔有,只要有一個線程佔有了共享鎖,全部線程(包括本身)都將沒法再獲取互斥鎖,可是能夠獲取共享鎖。

AQS隊列

AQS中維護了一個隊列,獲取鎖失敗(非tryLock())的線程都將進入這個隊列中排隊,等待鎖釋放後喚醒下一個排隊的線程(互斥鎖模式下)。

Condition隊列

AQS中還有另外一個很是重要的內部類ConditionObject,它實現了Condition接口,主要用於實現條件鎖。

ConditionObject中也維護了一個隊列,這個隊列主要用於等待條件的成立,當條件成立時,其它線程將signal這個隊列中的元素,將其移動到AQS的隊列中,等待佔有鎖的線程釋放鎖後被喚醒。

Condition典型的運用場景是在BlockingQueue中的實現,當隊列爲空時,獲取元素的線程阻塞在notEmpty條件上,一旦隊列中添加了一個元素,將通知notEmpty條件,將其隊列中的元素移動到AQS隊列中等待被喚醒。

模板方法

AQS這個抽象類把模板方法設計模式運用地爐火純青,它裏面定義了一系列的模板方法,好比下面這些:

// 獲取互斥鎖
public final void acquire(int arg) {
    // tryAcquire(arg)須要子類實現
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}
// 獲取互斥鎖可中斷
public final void acquireInterruptibly(int arg)
        throws InterruptedException {
    if (Thread.interrupted())
        throw new InterruptedException();
    // tryAcquire(arg)須要子類實現
    if (!tryAcquire(arg))
        doAcquireInterruptibly(arg);
}    
// 獲取共享鎖
public final void acquireShared(int arg) {
    // tryAcquireShared(arg)須要子類實現
    if (tryAcquireShared(arg) < 0)
     doAcquireShared(arg);
}
// 獲取共享鎖可中斷
public final void acquireSharedInterruptibly(int arg)
        throws InterruptedException {
    if (Thread.interrupted())
        throw new InterruptedException();
    // tryAcquireShared(arg)須要子類實現
    if (tryAcquireShared(arg) < 0)
        doAcquireSharedInterruptibly(arg);
}
// 釋放互斥鎖
public final boolean release(int arg) {
    // tryRelease(arg)須要子類實現
    if (tryRelease(arg)) {
        Node h = head;
        if (h != null && h.waitStatus != 0)
            unparkSuccessor(h);
        return true;
    }
    return false;
}
// 釋放共享鎖
public final boolean releaseShared(int arg) {
    // tryReleaseShared(arg)須要子類實現
    if (tryReleaseShared(arg)) {
        doReleaseShared();
        return true;
    }
    return false;
}

獲取鎖、釋放鎖的這些方法基本上都穿插在ReentrantLock、ReentrantReadWriteLock、Semaphore、CountDownLatch的源碼解析中了,如今看他們是否是舒服多了,若是一開始就看這些源碼,不免會很暈。

須要子類實現的方法

上面一塊兒學習了AQS中幾個重要的模板方法,下面咱們再一塊兒學習下幾個須要子類實現的方法:

// 互斥模式下使用:嘗試獲取鎖
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();
}

這幾個方法爲何不直接定義成抽象方法呢?

由於子類只要實現這幾個方法中的一部分就能夠實現一個同步器了,因此不須要定義成抽象方法。

總結

今天咱們大概講了下AQS中幾個重要的組成部分,搞明白了這幾個結構,AQS對你將沒有任何祕密可言,固然面試的時候能把這幾個點答清楚,面試官也會眼前一亮的。

(1)狀態變量state;

(2)AQS隊列;

(3)Condition隊列;

(4)模板方法;

(5)須要子類實現的方法;

彩蛋

通過前面的學習,您能簡要描述一下AQS獲取互斥鎖的大致流程嗎?

這裏彤哥就不做答了,相信學習完前面的內容,答這道題不是個問題了,答不上來的還須要把下面的推薦閱讀好好多看幾遍^^

推薦閱讀

一、 死磕 java同步系列之開篇

二、 死磕 java魔法類之Unsafe解析

三、 死磕 java同步系列之JMM(Java Memory Model)

四、 死磕 java同步系列之volatile解析

五、 死磕 java同步系列之synchronized解析

六、 死磕 java同步系列之本身動手寫一個鎖Lock

七、 死磕 java同步系列之AQS起篇

八、 死磕 java同步系列之ReentrantLock源碼解析(一)——公平鎖、非公平鎖

九、 死磕 java同步系列之ReentrantLock源碼解析(二)——條件鎖

十、 死磕 java同步系列之ReentrantLock VS synchronized

十一、 死磕 java同步系列之ReentrantReadWriteLock源碼解析

十二、 死磕 java同步系列之Semaphore源碼解析

1三、 死磕 java同步系列之CountDownLatch源碼解析

歡迎關注個人公衆號「彤哥讀源碼」,查看更多源碼系列文章, 與彤哥一塊兒暢遊源碼的海洋。

qrcode

相關文章
相關標籤/搜索