(1)AQS的定位?java
(2)AQS的重要組成部分?面試
(3)AQS運用的設計模式?設計模式
(4)AQS的整體流程?框架
AQS的全稱是AbstractQueuedSynchronizer,它的定位是爲Java中幾乎全部的鎖和同步器提供一個基礎框架。學習
在以前的章節中,咱們一塊兒學習了ReentrantLock、ReentrantReadWriteLock、Semaphore、CountDownLatch的源碼,今天咱們一塊兒來對AQS作個總結。ui
AQS中定義了一個狀態變量state,它有如下兩種使用方法:線程
(1)互斥鎖設計
當AQS只實現爲互斥鎖的時候,每次只要原子更新state的值從0變爲1成功了就獲取了鎖,可重入是經過不斷把state原子更新加1實現的。code
(2)互斥鎖 + 共享鎖接口
當AQS須要同時實現爲互斥鎖+共享鎖的時候,低16位存儲互斥鎖的狀態,高16位存儲共享鎖的狀態,主要用於實現讀寫鎖。
互斥鎖是一種獨佔鎖,每次只容許一個線程獨佔,且當一個線程獨佔時,其它線程將沒法再獲取互斥鎖及共享鎖,可是它本身能夠獲取共享鎖。
共享鎖同時容許多個線程佔有,只要有一個線程佔有了共享鎖,全部線程(包括本身)都將沒法再獲取互斥鎖,可是能夠獲取共享鎖。
AQS中維護了一個隊列,獲取鎖失敗(非tryLock())的線程都將進入這個隊列中排隊,等待鎖釋放後喚醒下一個排隊的線程(互斥鎖模式下)。
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同步系列之JMM(Java Memory Model)
八、 死磕 java同步系列之ReentrantLock源碼解析(一)——公平鎖、非公平鎖
九、 死磕 java同步系列之ReentrantLock源碼解析(二)——條件鎖
十、 死磕 java同步系列之ReentrantLock VS synchronized
十一、 死磕 java同步系列之ReentrantReadWriteLock源碼解析
1三、 死磕 java同步系列之CountDownLatch源碼解析
歡迎關注個人公衆號「彤哥讀源碼」,查看更多源碼系列文章, 與彤哥一塊兒暢遊源碼的海洋。