一篇讀懂(空調(diào)aqs是什么意思啊)空調(diào)aqf什么意思,
1. AQS
1.1 AQS 簡單介紹
AQS 的全稱為(AbstractQueuedSynchronizer),這個類在 java.util.concurrent.locks 包下面。
AQS 是一個用來構(gòu)建鎖和同步器的框架,使用 AQS 能簡單且高效地構(gòu)造出應(yīng)用廣泛的大量的同步器, 比如我們提到的 ReentrantLock,Semaphore,其他的諸如 ReentrantReadWriteLock,SynchronousQueue,F(xiàn)utureTask(jdk1.7) 等等皆是基于 AQS 的。當(dāng)然,我們自己也能利用 AQS 非常輕松容易地構(gòu)造出符合我們自己需求的同步器。
1.2 AQS 原理
1.2.1 AQS 原理概覽
AQS 核心思想是,如果被請求的共享資源空閑,則將當(dāng)前請求資源的線程設(shè)置為有效的工作線程,并且將共享資源設(shè)置為鎖定狀態(tài)。如果被請求的共享資源被占用,那么就需要一套線程阻塞等待以及被喚醒 時鎖分配的機(jī)制,這個機(jī)制 AQS 是用 CLH 隊列鎖實現(xiàn)的,即將暫時獲取不到鎖的線程加入到隊列中。
看個 AQS (AbstractQueuedSynchronizer)原理圖:
AQS 使用一個 int 成員變量來表示同步狀態(tài),通過內(nèi)置的 FIFO 隊列來完成獲取資源線程的排隊工作。
AQS 使用 CAS 對該同步狀態(tài)進(jìn)行原子操作實現(xiàn)對其值的修改。
狀態(tài)信息通過 protected 類型的getState , setState , compareAndSetState 進(jìn)行操作
1.2.2 AQS 對資源的共享方式
AQS 定義兩種資源共享方式
1) Exclusive(獨(dú)占)
只有一個線程能執(zhí)行,如 ReentrantLock。又可分為公平鎖和非公平鎖,ReentrantLock 同時支持兩種鎖,下面以 ReentrantLock 對這兩種鎖的定義做介紹:
下面來看 ReentrantLock 中相關(guān)的源代碼:
ReentrantLock 默認(rèn)采用非公平鎖,因為考慮獲得更好的性能,通過 boolean 來決定是否用公平鎖(傳入 true 用公平鎖)。

ReentrantLock 中公平鎖的 lock 方法


非公平鎖的 lock 方法:


總結(jié):公平鎖和非公平鎖只有兩處不同:

公平鎖和非公平鎖就這兩點(diǎn)區(qū)別,如果這兩次 CAS 都不成功,那么后面非公平鎖和公平鎖是一樣的, 都要進(jìn)入到阻塞隊列等待喚醒。
相對來說,非公平鎖會有更好的性能,因為它的吞吐量比較大。當(dāng)然,非公平鎖讓獲取鎖的時間變得更加不確定,可能會導(dǎo)致在阻塞隊列中的線程長期處于饑餓狀態(tài)。
1) Share(共享)

1.2.3 AQS 底層使用了模板方法模式

AQS 使用了模板方法模式,自定義同步器時需要重寫下面幾個 AQS 提供的模板方法:


1.3 Semaphore(信號量)-允許多個線程同時訪問
synchronized 和 ReentrantLock 都是一次只允許一個線程訪問某個資源,Semaphore(信號量)可以指定多個線程同時訪問某個資源。
示例代碼如下:


執(zhí)行 acquire 方法阻塞,直到有一個許可證可以獲得然后拿走一個許可證;每個 rerelease方法增加一個許可證,這可能會釋放一個阻塞的 acquire 方法。然而,其實并沒有實際的許可證這個對象,Semaphore 只是維持了一個可獲得許可證的數(shù)量。 Semaphore 經(jīng)常用于限制獲取某種資源的線程數(shù)量。
當(dāng)然一次也可以一次拿取和釋放多個許可,不過一般沒有必要這樣做:

除了 acquire 方法之外,另一個比較常用的與之對應(yīng)的方法是tryAcquire 方法,該方法如果獲取不到許可就立即返回 false。
Semaphore 有兩種模式,公平模式和非公平模式。
公平模式: 調(diào)用 acquire 的順序就是獲取許可證的順序,遵循 FIFO;
非公平模式: 搶占式的。
Semaphore 對應(yīng)的兩個構(gòu)造方法如下:

這兩個構(gòu)造方法,都必須提供許可的數(shù)量,第二個構(gòu)造方法可以指定是公平模式還是非公平模式,默認(rèn)非公平模式。
補(bǔ)充:Semaphore與CountDownLatch一樣,也是共享鎖的一種實現(xiàn)。它默認(rèn)構(gòu)造AQS的state為permits。當(dāng)執(zhí)行任務(wù)的線程數(shù)量超出permits,那么多余的線程將會被放入阻塞隊列Park,并自旋判斷state是否大于0。只有當(dāng)state大于0的時候,阻塞的線程才能繼續(xù)執(zhí)行,此時先前執(zhí)行任務(wù)的線程繼續(xù)執(zhí) 行release方法,release方法使得state的變量會加1,那么自旋的線程便會判斷成功。如此,每次只有最多不超過permits數(shù)量的線程能自旋成功,便限制了執(zhí)行任務(wù)線程的數(shù)量。
1.4 CountDownLatch (倒計時器)

1.4.1 CountDownLatch 的兩種典型用法

1.4.2 CountDownLatch 的使用示例


上面的代碼中,我們定義了請求的數(shù)量為 550,當(dāng)這 550 個請求被處理完成之后,才會執(zhí)行
System.out.println("finish");
與 CountDownLatch 的第一次交互是主線程等待其他線程。主線程必須在啟動其他線程后立即調(diào)用 CountDownLatch.await()方法。這樣主線程的操作就會在這個方法上阻塞,直到其他線程完成各自的任務(wù)。
其他 N 個線程必須引用閉鎖對象,因為他們需要通知CountDownLatch對象,他們已經(jīng)完成了各自的任務(wù)。這種通知機(jī)制是通過CountDownLatch.countDown()方法來完成的;每調(diào)用一次這個方法,在構(gòu)造函數(shù)中初始化的 count 值就減 1。所以當(dāng) N 個線程都調(diào) 用了這個方法,count 的值等于 0,然后主線程就能通過 await() 方法,恢復(fù)執(zhí)行自己的任務(wù)。
再插一嘴: CountDownLatch 的await()方法使用不當(dāng)很容易產(chǎn)生死鎖,比如我們上面代碼中的 for循環(huán)改為:
這樣就導(dǎo)致 count 的值沒辦法等于 0,然后就會導(dǎo)致一直等待。
1.4.3 CountDownLatch 的不足
CountDownLatch 是一次性的,計數(shù)器的值只能在構(gòu)造方法中初始化一次,之后沒有任何機(jī)制再次對其設(shè)置值,當(dāng) CountDownLatch 使用完畢后,它不能再次被使用。
1.4.4 CountDownLatch 項常見面試題
解釋一下 CountDownLatch 概念?
CountDownLatch 和 CyclicBarrier 的不同之處?
給出一些 CountDownLatch 使用的例子?
CountDownLatch 類中主要的方法?
1.5 CyclicBarrier(循環(huán)柵欄)
CyclicBarrier 和 CountDownLatch 非常類似,它也可以實現(xiàn)線程間的技術(shù)等待,但是它的功能比CountDownLatch 更加復(fù)雜和強(qiáng)大。主要應(yīng)用場景和 CountDownLatch 類似。
CountDownLatch的實現(xiàn)是基于AQS的,而CycliBarrier是基于 ReentrantLock(ReentrantLock也屬于AQS同步器)和 Condition 的.
CyclicBarrier 的字面意思是可循環(huán)使用(Cyclic)的屏障(Barrier)。它要做的事情是,讓一組線程到達(dá)一個屏障(也可以叫同步點(diǎn))時被阻塞,直到最后一個線程到達(dá)屏障時,屏障才會開門,所有被屏障 攔截的線程才會繼續(xù)干活。CyclicBarrier 默認(rèn)的構(gòu)造方法是 CyclicBarrier(int parties) ,其參數(shù)表示屏障攔截的線程數(shù)量,每個線程調(diào)用await 方法告訴 CyclicBarrier 我已經(jīng)到達(dá)了屏障,然后當(dāng)前線程被阻塞。
再來看一下它的構(gòu)造函數(shù):
其中,parties 就代表了有攔截的線程的數(shù)量,當(dāng)攔截的線程數(shù)量達(dá)到這個值的時候就打開柵欄,讓所有線程通過。
1.5.1 CyclicBarrier 的應(yīng)用場景
1.5.2 CyclicBarrier 的使用示例
示例 1:
運(yùn)行結(jié)果,如下:
可以看到當(dāng)線程數(shù)量也就是請求數(shù)量達(dá)到我們定義的 5 個的時候, await 方法之后的方法才被執(zhí)行。
另外,CyclicBarrier還提供一個更高級的構(gòu)造函數(shù)CyclicBarrier(intparties,RunnablebarrierAction),用于在線程到達(dá)屏障時,優(yōu)先執(zhí)行barrierAction,方便處理更復(fù)雜的業(yè)務(wù)場景。示例代碼如下:
運(yùn)行結(jié)果,如下:
1.5.3 CyclicBarrier 源碼分析
dowait(false, 0L) :
總結(jié): CyclicBarrier 內(nèi)部通過一個 count 變量作為計數(shù)器,cout 的初始值為 parties 屬性的初始化值,每當(dāng)一個線程到了柵欄這里了,那么就將計數(shù)器減一。如果 count 值為 0 了,表示這是這一代最后一個線程到達(dá)柵欄,就嘗試執(zhí)行我們構(gòu)造方法中輸入的任務(wù)。
1.5.4CyclicBarrier 和 CountDownLatch 的區(qū)別
下面這個是國外一個大佬的回答:
CountDownLatch 是計數(shù)器,只能使用一次,而 CyclicBarrier 的計數(shù)器提供 reset 功能,可以多次使用。但是我不那么認(rèn)為它們之間的區(qū)別僅僅就是這么簡單的一點(diǎn)。我們來從 jdk 作者設(shè)計的目的來看,javadoc 是這么描述它們的:
對于 CountDownLatch 來說,重點(diǎn)是“一個線程(多個線程)等待”,而其他的 N 個線程在完成“某件事情”之后,可以終止,也可以等待。而對于 CyclicBarrier,重點(diǎn)是多個線程,在任意一個線程沒有完成,所有的線程都必須等待。
CountDownLatch 是計數(shù)器,線程完成一個記錄一個,只不過計數(shù)不是遞增而是遞減,而
CyclicBarrier 更像是一個閥門,需要所有線程都到達(dá),閥門才能打開,然后繼續(xù)執(zhí)行。
1.6 ReentrantLock 和 ReentrantReadWriteLock
ReentrantLock 和 synchronized 的區(qū)別在上面已經(jīng)講過了這里就不多做講解。另外,需要注意的是: 讀寫鎖 ReentrantReadWriteLock 可以保證多個線程可以同時讀,所以在讀操作遠(yuǎn)大于寫操作的時候, 讀寫鎖就非常有用了。