并發(fā)編程-(6)-AQS原理、一步一步圖解Exclusive模式獲得所&釋放鎖內(nèi)存變化、ReentrantLock:acquire & release 、喚醒節(jié)點為何要倒序遍歷同步隊列?

死神--->一番隊隊長山本元柳齋重國

目錄:

  • 1、Lock介紹
  • 2、Lock的實現(xiàn)類ReentrantLock
  • 3、AQS簡介
  • 4、ReentrantLock類體系
  • 5 、Lock使用
  • 6、AQS原理:
    • 6.1、AQS內(nèi)存模型
    • 6.2、AQS類體系
    • 6.3、AQS模板方法
    • 6.4、Node節(jié)點
    • 6.5、CHL同步隊列
    • 6.6、同步狀態(tài)獲?。╝cquire)與釋放(release)
    • 6.7、線程阻塞與喚醒
  • 7、AQS-產(chǎn)品之一ReentrantLock
    • 7.1、圖解:獲取鎖、釋放鎖、入同步隊列
    • 7.2、公平鎖&非公平鎖
  • 8、面試常見問題
    • 1、公平鎖(FairSync)和非公平鎖(NonfairSync)的區(qū)別?
    • 2、ReentrantLock如何處理線程中斷的?
    • 3、未完待續(xù)~


1、Lock介紹

Lock 接口實現(xiàn)類提供了比使用 synchronized 方法和語句可獲得的更廣泛的鎖定操作。此實現(xiàn)允許更靈活的結(jié)構(gòu),可以具有差別很大的屬性,可以支持多個相關(guān)的 Condition 對象(Condition實現(xiàn)類ConditonObject來實現(xiàn)線程的通知/與喚醒機(jī)制,關(guān)于Condition傳送門
)。

2、 Lock的實現(xiàn)類ReentrantLock

ReentrantLock是根據(jù)AQS實現(xiàn)的獨占鎖(exclusive)。

3、 AQS簡介

AQS全稱AbstractQueuedSynchronizer,是java并發(fā)包中的一個類,該類更像是一個框架(模板方法模式),提供了一些模板方法供子類實現(xiàn),從而實現(xiàn)了不同的同步器,如下圖所示。ReentrantLock,ReentrantReadWriteLock,CountDownLatch、ThreadPoolExecutor等 這些常見類都使用了AQS。

4、 ReentrantLock類體系

ReentrantLock類體系結(jié)構(gòu)

5、Lock使用

eg:加鎖/解鎖偽代碼
Lock lock = new xxxLock();
try{
  // 加鎖
  lock.lock();
  balabala作相應(yīng)業(yè)務(wù)
} finally{
  // 解鎖
  lock.unlock();
}

6、AQS原理:

6.1、AQS內(nèi)存模型
AQS內(nèi)存模型
6.2、AQS類體系
AQS類體系結(jié)構(gòu)
6.3、AQS模板方法
模板方法

抽象方法
6.4、Node節(jié)點
Node節(jié)點
6.5、CHL同步隊列
CHL同步隊列
6.6、同步狀態(tài)獲?。╝cquire)與釋放(release)
獲取&釋放同步狀態(tài)
6.7、線程阻塞與喚醒
線程掛起&喚醒

7、AQS-產(chǎn)品之一ReentrantLock:

7.1、圖解:獲取鎖、釋放鎖、入同步隊列
背景:Thread-A 、Thread-B、Thread-C順序獲取鎖,模擬內(nèi)存中同步隊列變化。還要一種特殊情況:Thread-D獲取鎖失敗,同時入同步隊列失敗(最倒霉的線程D)。
Thread-A獲得重入鎖

Thread-B獲得鎖失敗,入同步隊列

Thread-C入同步隊列
最倒霉的Thread-D加鎖失敗、入隊失敗g

如果node!=tail,不是尾節(jié)點,尾結(jié)點是Thread-X

走uparkSuccessor(node)

假設(shè)內(nèi)存中Thread-N中的 waitStatus=1
分析下第7-3步驟為何要倒序循環(huán)喚醒節(jié)點?
為何倒序喚醒節(jié)點?
7.2、公平鎖&非公平鎖

thread-6公平鎖入隊

thread-6非公平鎖入隊

8、面試常見問題:

  • 1、公平鎖(FairSync)和非公平鎖(NonfairSync)的區(qū)別?
    鎖的公平性:獲取鎖順序而言的。
    共同點:都是繼承自ReentrantLock內(nèi)部類Sync。
    差異:
    • 公平鎖,那么鎖的獲取順序就應(yīng)該符合請求的絕對時間順序,也就是 FIFO。
    • 非公平鎖,在獲取鎖的時候,會先通過 CAS 進(jìn)行搶占。
      另外公平鎖判斷條件多了 hasQueuedPredecessors()方法,也就是加入了[同步隊列中當(dāng)前節(jié)點是否有前驅(qū)節(jié)點]的判斷,如果該方法返回 true,則表示有線程比當(dāng)前線程更早地請求獲取鎖, 因此需要等待前驅(qū)線程獲取并釋放鎖之后才能繼續(xù)獲取鎖。
      公平鎖加鎖方法:
// 公平鎖-獲取鎖的方法
     protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                // 同步隊列中當(dāng)前節(jié)點是否有前驅(qū)節(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;
    }

非公平鎖加鎖方法:

        final void lock() {
            // 嘗試CAS加鎖
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                // 第一次嘗試失敗后,走父類通用加鎖邏輯
                acquire(1);
        }
        // 父類模板方法回調(diào)tryAcquire方法
        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;
        }
  • 2、ReentrantLock如何處理線程中斷的?
    阻塞隊列使用的LockSupport.park();
private final boolean parkAndCheckInterrupt() {
        LockSupport.park(this);
        return Thread.interrupted();
    }

因為LockSupport.park(),無法響應(yīng)Thread.interrupt(); 所以當(dāng)unpark()后使用Thread.interrupted()來判斷線程是否有中斷過。如果中斷過整個喚醒的線程在外層方法會繼續(xù)執(zhí)行一次中斷,詳情源碼如下:


image.png
image.png
static void selfInterrupt() {
        Thread.currentThread().interrupt();
    }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容