這篇文章將為大家詳細(xì)講解有關(guān)Java并發(fā)工具怎么用,小編覺(jué)得挺實(shí)用的,因此分享給大家做個(gè)參考,希望大家閱讀完這篇文章后可以有所收獲。
讓客戶滿意是我們工作的目標(biāo),不斷超越客戶的期望值來(lái)自于我們對(duì)這個(gè)行業(yè)的熱愛(ài)。我們立志把好的技術(shù)通過(guò)有效、簡(jiǎn)單的方式提供給客戶,將通過(guò)不懈努力成為客戶在信息化領(lǐng)域值得信任、有價(jià)值的長(zhǎng)期合作伙伴,公司提供的服務(wù)項(xiàng)目有:域名注冊(cè)、網(wǎng)站空間、營(yíng)銷軟件、網(wǎng)站建設(shè)、云和網(wǎng)站維護(hù)、網(wǎng)站推廣。
并發(fā)工具是一組工具類,主要是用來(lái)控制線程的執(zhí)行流程,比如阻塞某個(gè)線程,以等待其他線程
從字面意思來(lái)看,就是一個(gè)倒計(jì)數(shù)門(mén)閂(shuan,打了半天zha就是打不出來(lái))
通俗一點(diǎn)來(lái)說(shuō),就是倒計(jì)數(shù),時(shí)間一到,門(mén)閂就打開(kāi)
注:一旦打開(kāi),就不能再合上,即這個(gè) CountDownLatch 的狀態(tài)改變是永久不可恢復(fù)的(記住這個(gè)點(diǎn),后面會(huì)有對(duì)比)
比較官方的說(shuō)法:倒計(jì)數(shù)器用來(lái)阻塞某個(gè)(某些)線程,以等待其他多個(gè)線程的任務(wù)執(zhí)行完成(以這個(gè)說(shuō)法為準(zhǔn),上面的可以用來(lái)對(duì)比參考)
下面列出 CountDownLatch 的幾個(gè)方法:
構(gòu)造方法:public CountDownLatch(int count)
,其中count就是我們所說(shuō)的內(nèi)部狀態(tài)(當(dāng)count=0時(shí),表示到達(dá)終止?fàn)顟B(tài),此時(shí)會(huì)恢復(fù)被阻塞的線程)
修改狀態(tài):public void countDown()
,該方法會(huì)遞減上面的count狀態(tài),每執(zhí)行一次,就-1;(當(dāng)count=0時(shí),表示到達(dá)終止?fàn)顟B(tài),此時(shí)會(huì)恢復(fù)被阻塞的線程)
等待:public void await()
,該方法會(huì)阻塞當(dāng)前線程,直到count狀態(tài)變?yōu)?,才會(huì)恢復(fù)執(zhí)行(除非中斷,此時(shí)會(huì)拋出中斷異常)
超時(shí)等待:public boolean await(long timeout, TimeUnit unit)
,類似上面的await,只不過(guò)可以設(shè)置超時(shí)時(shí)間,等過(guò)了超時(shí)時(shí)間,還在阻塞,則直接恢復(fù)
獲取狀態(tài)值 count:public long getCount()
,獲取count的數(shù)值,以查看還可以遞減多少次(多用來(lái)調(diào)試)
模擬場(chǎng)景的話,這里先列舉三個(gè),肯定還有其他的
第一個(gè)就是計(jì)數(shù)器了,最直接的
第二個(gè)就是統(tǒng)計(jì)任務(wù)執(zhí)行時(shí)長(zhǎng)
第三個(gè)就是多人5V5游戲,等所有人加載完畢,就開(kāi)始游戲
下面我們以第三個(gè)場(chǎng)景為例,寫(xiě)個(gè)例子:多人游戲加載畫(huà)面
public class CountDownLatchDemo { public static void main(String[] args) throws InterruptedException { // 1. 構(gòu)造一個(gè)倒計(jì)數(shù)器,給定一個(gè)狀態(tài)值10 CountDownLatch latch = new CountDownLatch(10); System.out.println("準(zhǔn)備加載"); // 這里我們創(chuàng)建10個(gè)線程,模擬 5V5 游戲的10個(gè)玩家 for (int i = 0; i < 10; i++) { new Thread(()->{ // 這里我們給點(diǎn)延時(shí),模擬網(wǎng)絡(luò)延時(shí) try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"加載100%"); // 2. 這里的countDown就是用來(lái)改變倒計(jì)數(shù)器的內(nèi)部狀態(tài),每次-1 latch.countDown(); //這里不會(huì)阻塞當(dāng)前線程,執(zhí)行完后就立馬返回了 }).start(); } // 3. 這里阻塞等待狀態(tài)的完成,即10變?yōu)?; latch.await(); System.out.println("所有人加載完成,開(kāi)始游戲"); } }
輸出如下:
準(zhǔn)備加載 Thread-0加載100% Thread-1加載100% Thread-2加載100% Thread-3加載100% Thread-4加載100% Thread-5加載100% Thread-6加載100% Thread-8加載100% Thread-9加載100% Thread-7加載100% 所有人加載完成,開(kāi)始游戲
這里倒計(jì)數(shù)器的作用就是阻塞主線程,以等待其他10個(gè)子線程,等到都準(zhǔn)備好,再恢復(fù)主線程
它的特點(diǎn)就是:一次性使用,達(dá)到終止?fàn)顟B(tài)后不能再改變
循環(huán)柵欄,類似倒計(jì)數(shù)器,也是用來(lái)阻塞線程,不過(guò)它的重點(diǎn)在于循環(huán)使用
而倒計(jì)數(shù)器只能用一次(這屬于他們之間最明顯的一個(gè)區(qū)別)
PS:猜測(cè)之所以叫循環(huán)柵欄,而不是循環(huán)門(mén)閂,可能是因?yàn)闁艡诘淖饔帽乳T(mén)閂更強(qiáng)大,所以叫柵欄更適合吧
官方說(shuō)法:循環(huán)柵欄一般用來(lái)表示多個(gè)線程之間的相互等待(阻塞)
比如有10個(gè)線程,都要await等待;那要等到最后一個(gè)線程await時(shí),柵欄才會(huì)打開(kāi)
如果有定義柵欄動(dòng)作,那么當(dāng)柵欄打開(kāi)時(shí),會(huì)執(zhí)行柵欄動(dòng)作
柵欄動(dòng)作就是:柵欄打開(kāi)后需執(zhí)行的動(dòng)作,通過(guò)構(gòu)造函數(shù)的Runnable參數(shù)指定,可選參數(shù),下面會(huì)介紹
這個(gè)屬于循環(huán)柵欄和倒計(jì)數(shù)器的第二個(gè)區(qū)別:
循環(huán)柵欄強(qiáng)調(diào)的是多個(gè)被阻塞線程之間的相互協(xié)作關(guān)系(等待)
而倒計(jì)數(shù)器強(qiáng)調(diào)的是單個(gè)(或多個(gè))線程被阻塞,來(lái)等待其他線程的任務(wù)執(zhí)行
下面我們看幾個(gè)循環(huán)柵欄 CyclicBarrier 內(nèi)部的方法:
構(gòu)造方法:public CyclicBarrier(int parties, Runnable barrierAction)
,第一個(gè)表示需等待(阻塞)的線程數(shù),第二個(gè)barrierAction就是上面我們說(shuō)的柵欄動(dòng)作,即當(dāng)最后一個(gè)線程也被阻塞時(shí),就會(huì)觸發(fā)這個(gè)柵欄動(dòng)作(這個(gè)參數(shù)可選,如果沒(méi)有,則不執(zhí)行任何動(dòng)作)
等待:public int await()
,阻塞當(dāng)前線程,直到最后一個(gè)線程被阻塞,才會(huì)恢復(fù)
超時(shí)等待:public boolean await(long timeout, TimeUnit unit)
,類似上面的await,只不過(guò)可以設(shè)置超時(shí)時(shí)間
獲取當(dāng)前等待的線程數(shù):public int getNumberWaiting()
,即調(diào)用了await方法的線程數(shù)量
場(chǎng)景:
大事化小,小事合并:就是將某個(gè)大任務(wù)拆解為多個(gè)小任務(wù),等到小任務(wù)都完成,再合并為一個(gè)結(jié)果
多人對(duì)戰(zhàn)游戲團(tuán)戰(zhàn)
上面的倒計(jì)數(shù)器表示游戲開(kāi)始前的準(zhǔn)備工作(只需準(zhǔn)備一次)
而這里的循環(huán)柵欄則可以表示游戲開(kāi)始后的團(tuán)戰(zhàn)工作(可團(tuán)戰(zhàn)多次)
下面看下例子:多人游戲團(tuán)戰(zhàn)畫(huà)面
public class CyclicBarrierDemo { public static void main(String[] args) throws InterruptedException { // 1. 創(chuàng)建一個(gè)循環(huán)柵欄,給定等待線程數(shù)10和柵欄動(dòng)作 CyclicBarrier barrier = new CyclicBarrier(10,()->{ // 柵欄動(dòng)作,等到所有線程都await,就會(huì)觸發(fā) System.out.println("=== 人齊了,開(kāi)始團(tuán)吧"); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } }); System.out.println("=== 準(zhǔn)備第一波團(tuán)戰(zhàn) ==="); // 2. 創(chuàng)建10個(gè)線程,模擬10個(gè)玩家 for (int i = 0; i < 10; i++) { new Thread(()->{ try { // 玩家到場(chǎng) System.out.println(Thread.currentThread().getName()+"=>第一波團(tuán),我準(zhǔn)備好了"); // 等待其他人,等人齊就可以團(tuán)了(人齊了會(huì)執(zhí)行柵欄動(dòng)作,此時(shí)這邊也會(huì)恢復(fù)執(zhí)行) barrier.await(); } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } }).start(); } // 3. 查詢當(dāng)前等待都線程數(shù)量,如果不為0,則主線程繼續(xù)等待 while (barrier.getNumberWaiting()!=0){ Thread.sleep(1000); } System.out.println("=== 第一波團(tuán)戰(zhàn)結(jié)束 ==="); // 4. 此時(shí)還可以進(jìn)行第二波第三波團(tuán)戰(zhàn)。。。(循環(huán)柵欄可循環(huán)觸發(fā),倒計(jì)數(shù)器只能觸發(fā)一次) } }
輸出如下:
=== 準(zhǔn)備第一波團(tuán)戰(zhàn) === Thread-0=>第一波團(tuán),我準(zhǔn)備好了 Thread-1=>第一波團(tuán),我準(zhǔn)備好了 Thread-2=>第一波團(tuán),我準(zhǔn)備好了 Thread-3=>第一波團(tuán),我準(zhǔn)備好了 Thread-4=>第一波團(tuán),我準(zhǔn)備好了 Thread-5=>第一波團(tuán),我準(zhǔn)備好了 Thread-6=>第一波團(tuán),我準(zhǔn)備好了 Thread-7=>第一波團(tuán),我準(zhǔn)備好了 Thread-8=>第一波團(tuán),我準(zhǔn)備好了 Thread-9=>第一波團(tuán),我準(zhǔn)備好了 === 人齊了,開(kāi)始團(tuán)吧 === 第一波團(tuán)戰(zhàn)結(jié)束 ===
信號(hào)量主要是用來(lái)控制多個(gè)線程同時(shí)訪問(wèn)指定資源,比如數(shù)據(jù)庫(kù)連接池,超過(guò)指定數(shù)量,就阻塞等待
下面我們介紹下信號(hào)量的幾個(gè)關(guān)鍵方法:
構(gòu)造方法:public Semaphore(int permits, boolean fair)
,第一個(gè)參數(shù)為許可數(shù),即允許同時(shí)訪問(wèn)的的線程數(shù),第二個(gè)參數(shù)為公平還是非公平模式(默認(rèn)非公平)
公平模式,誰(shuí)先調(diào)用acquire,誰(shuí)就先訪問(wèn)資源,F(xiàn)IFO先進(jìn)先出
非公平模式,允許插隊(duì),如果某個(gè)線程剛釋放了許可,另一個(gè)線程就調(diào)用了acquire,那么這個(gè)線程就會(huì)插隊(duì)訪問(wèn)資源)
獲取許可:public void acquire()
,如果有許可,則直接返回,并將許可數(shù)遞減1;如果沒(méi)可用的許可,就阻塞等待,或者被中斷
嘗試獲取許可:public boolean tryAcquire()
,類似上面的acquire,但是不會(huì)被阻塞和中斷,因?yàn)槿绻麤](méi)有可用的許可,則直接返回false
釋放許可:public void release()
,釋放一個(gè)許可,并將許可數(shù)遞增1
獲取可用的許可數(shù)量:public int availablePermits()
,這個(gè)方法一般用來(lái)調(diào)試
場(chǎng)景:數(shù)據(jù)庫(kù)連接池
信號(hào)量的特點(diǎn)就是可重復(fù)使用許可,所以像數(shù)據(jù)庫(kù)連接池這種場(chǎng)景就很適合了
這里就不舉例子了,就是多個(gè)線程acquire和release,獲取許可時(shí),如果沒(méi)有就阻塞,如果有就立即返回
用表格看比較方便點(diǎn)
區(qū)別 | CountDownLatch | CyclicBarrier | Semaphore |
---|---|---|---|
可使用次數(shù) | 單次 | 多次(循環(huán)使用) | 多次(循環(huán)使用) |
線程的阻塞 | 阻塞單個(gè)(多個(gè))線程,以等待其他線程的執(zhí)行 | 多個(gè)線程之間的相互阻塞 | 超過(guò)許可數(shù),會(huì)阻塞 |
場(chǎng)景 | 1. 計(jì)數(shù)器 2. 統(tǒng)計(jì)任務(wù)執(zhí)行時(shí)長(zhǎng) 3. 多人對(duì)戰(zhàn)游戲的開(kāi)局等待 | 1. 大事化小,再合并 2. 多人對(duì)戰(zhàn)游戲的團(tuán)戰(zhàn) | 1. 數(shù)據(jù)庫(kù)連接池 |
可以看到,倒計(jì)數(shù)器主要是用來(lái)表示單個(gè)線程等待多個(gè)線程,而循環(huán)柵欄主要是用來(lái)表示多個(gè)線程之間的相互等待
什么是并發(fā)工具:并發(fā)工具是一組工具類,主要是用來(lái)控制線程的執(zhí)行流程,比如阻塞某個(gè)線程,以等待其他線程
倒計(jì)數(shù)器 CountDownLatch:用來(lái)表示阻塞某個(gè)(某些)線程,以等待其他多個(gè)線程的任務(wù)執(zhí)行完成
循環(huán)柵欄 CyclicBarrier:用來(lái)表示多個(gè)線程之間的相互等待協(xié)作(阻塞)
信號(hào)量 Semaphore:用來(lái)表示允許同時(shí)訪問(wèn)指定資源的許可數(shù)(線程數(shù))
區(qū)別:
區(qū)別 | CountDownLatch | CyclicBarrier | Semaphore |
---|---|---|---|
可使用次數(shù) | 單次 | 多次(循環(huán)使用) | 多次(循環(huán)使用) |
線程的阻塞 | 阻塞單個(gè)(多個(gè))線程,以等待其他線程的執(zhí)行 | 多個(gè)線程之間的相互阻塞 | 超過(guò)許可數(shù),會(huì)阻塞 |
場(chǎng)景 | 1. 計(jì)數(shù)器 2. 統(tǒng)計(jì)任務(wù)執(zhí)行時(shí)長(zhǎng) 3. 多人對(duì)戰(zhàn)游戲的開(kāi)局等待 | 1. 大事化小,再合并 2. 多人對(duì)戰(zhàn)游戲的團(tuán)戰(zhàn) | 1. 數(shù)據(jù)庫(kù)連接池 |
關(guān)于“Java并發(fā)工具怎么用”這篇文章就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,使各位可以學(xué)到更多知識(shí),如果覺(jué)得文章不錯(cuò),請(qǐng)把它分享出去讓更多的人看到。