溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Java并發(fā)工具怎么用

發(fā)布時(shí)間:2021-10-11 11:01:11 來源:億速云 閱讀:101 作者:小新 欄目:編程語言

這篇文章將為大家詳細(xì)講解有關(guān)Java并發(fā)工具怎么用,小編覺得挺實(shí)用的,因此分享給大家做個(gè)參考,希望大家閱讀完這篇文章后可以有所收獲。

正文

1. 什么是并發(fā)工具

并發(fā)工具是一組工具類,主要是用來控制線程的執(zhí)行流程,比如阻塞某個(gè)線程,以等待其他線程

2. 倒計(jì)數(shù)器 CountDownLatch

從字面意思來看,就是一個(gè)倒計(jì)數(shù)門閂(shuan,打了半天zha就是打不出來)

通俗一點(diǎn)來說,就是倒計(jì)數(shù),時(shí)間一到,門閂就打開

注:一旦打開,就不能再合上,即這個(gè) CountDownLatch 的狀態(tài)改變是永久不可恢復(fù)的(記住這個(gè)點(diǎn),后面會(huì)有對比)

比較官方的說法:倒計(jì)數(shù)器用來阻塞某個(gè)(某些)線程,以等待其他多個(gè)線程的任務(wù)執(zhí)行完成(以這個(gè)說法為準(zhǔn),上面的可以用來對比參考)

下面列出 CountDownLatch 的幾個(gè)方法:

  • 構(gòu)造方法public CountDownLatch(int count),其中count就是我們所說的內(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,只不過可以設(shè)置超時(shí)時(shí)間,等過了超時(shí)時(shí)間,還在阻塞,則直接恢復(fù)

  • 獲取狀態(tài)值 countpublic long getCount(),獲取count的數(shù)值,以查看還可以遞減多少次(多用來調(diào)試)

模擬場景的話,這里先列舉三個(gè),肯定還有其他的

  • 第一個(gè)就是計(jì)數(shù)器了,最直接的

  • 第二個(gè)就是統(tǒng)計(jì)任務(wù)執(zhí)行時(shí)長

  • 第三個(gè)就是多人5V5游戲,等所有人加載完畢,就開始游戲

下面我們以第三個(gè)場景為例,寫個(gè)例子:多人游戲加載畫面

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就是用來改變倒計(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("所有人加載完成,開始游戲");
    }
}

輸出如下:

準(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%
所有人加載完成,開始游戲

這里倒計(jì)數(shù)器的作用就是阻塞主線程,以等待其他10個(gè)子線程,等到都準(zhǔn)備好,再恢復(fù)主線程

它的特點(diǎn)就是:一次性使用,達(dá)到終止?fàn)顟B(tài)后不能再改變

3. 倒計(jì)數(shù)器升級(jí)版 CyclicBarrier【循環(huán)柵欄】

循環(huán)柵欄,類似倒計(jì)數(shù)器,也是用來阻塞線程,不過它的重點(diǎn)在于循環(huán)使用

而倒計(jì)數(shù)器只能用一次(這屬于他們之間最明顯的一個(gè)區(qū)別)

PS:猜測之所以叫循環(huán)柵欄,而不是循環(huán)門閂,可能是因?yàn)闁艡诘淖饔帽乳T閂更強(qiáng)大,所以叫柵欄更適合吧

官方說法:循環(huán)柵欄一般用來表示多個(gè)線程之間的相互等待(阻塞)

比如有10個(gè)線程,都要await等待;那要等到最后一個(gè)線程await時(shí),柵欄才會(huì)打開

如果有定義柵欄動(dòng)作,那么當(dāng)柵欄打開時(shí),會(huì)執(zhí)行柵欄動(dòng)作

柵欄動(dòng)作就是:柵欄打開后需執(zhí)行的動(dòng)作,通過構(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è))線程被阻塞,來等待其他線程的任務(wù)執(zhí)行

下面我們看幾個(gè)循環(huán)柵欄 CyclicBarrier 內(nèi)部的方法:

  • 構(gòu)造方法public CyclicBarrier(int parties, Runnable barrierAction),第一個(gè)表示需等待(阻塞)的線程數(shù),第二個(gè)barrierAction就是上面我們說的柵欄動(dòng)作,即當(dāng)最后一個(gè)線程也被阻塞時(shí),就會(huì)觸發(fā)這個(gè)柵欄動(dòng)作(這個(gè)參數(shù)可選,如果沒有,則不執(zhí)行任何動(dòng)作)

  • 等待public int await(),阻塞當(dāng)前線程,直到最后一個(gè)線程被阻塞,才會(huì)恢復(fù)

  • 超時(shí)等待public boolean await(long timeout, TimeUnit unit),類似上面的await,只不過可以設(shè)置超時(shí)時(shí)間

  • 獲取當(dāng)前等待的線程數(shù)public int getNumberWaiting(),即調(diào)用了await方法的線程數(shù)量

場景:

  • 大事化小,小事合并:就是將某個(gè)大任務(wù)拆解為多個(gè)小任務(wù),等到小任務(wù)都完成,再合并為一個(gè)結(jié)果

  • 多人對戰(zhàn)游戲團(tuán)戰(zhàn)

    • 上面的倒計(jì)數(shù)器表示游戲開始前的準(zhǔn)備工作(只需準(zhǔn)備一次)

    • 而這里的循環(huán)柵欄則可以表示游戲開始后的團(tuán)戰(zhàn)工作(可團(tuán)戰(zhàn)多次)

下面看下例子:多人游戲團(tuán)戰(zhàn)畫面

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("=== 人齊了,開始團(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 {
                    // 玩家到場
                    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)備好了
=== 人齊了,開始團(tuán)吧
=== 第一波團(tuán)戰(zhàn)結(jié)束 ===

4. 信號(hào)量 Semaphore

信號(hào)量主要是用來控制多個(gè)線程同時(shí)訪問指定資源,比如數(shù)據(jù)庫連接池,超過指定數(shù)量,就阻塞等待

下面我們介紹下信號(hào)量的幾個(gè)關(guān)鍵方法:

  • 構(gòu)造方法:public Semaphore(int permits, boolean fair),第一個(gè)參數(shù)為許可數(shù),即允許同時(shí)訪問的的線程數(shù),第二個(gè)參數(shù)為公平還是非公平模式(默認(rèn)非公平)

    • 公平模式,誰先調(diào)用acquire,誰就先訪問資源,F(xiàn)IFO先進(jìn)先出

    • 非公平模式,允許插隊(duì),如果某個(gè)線程剛釋放了許可,另一個(gè)線程就調(diào)用了acquire,那么這個(gè)線程就會(huì)插隊(duì)訪問資源)

  • 獲取許可:public void acquire(),如果有許可,則直接返回,并將許可數(shù)遞減1;如果沒可用的許可,就阻塞等待,或者被中斷

  • 嘗試獲取許可:public boolean tryAcquire(),類似上面的acquire,但是不會(huì)被阻塞和中斷,因?yàn)槿绻麤]有可用的許可,則直接返回false

  • 釋放許可:public void release() ,釋放一個(gè)許可,并將許可數(shù)遞增1

  • 獲取可用的許可數(shù)量:public int availablePermits() ,這個(gè)方法一般用來調(diào)試

場景:數(shù)據(jù)庫連接池

信號(hào)量的特點(diǎn)就是可重復(fù)使用許可,所以像數(shù)據(jù)庫連接池這種場景就很適合了

這里就不舉例子了,就是多個(gè)線程acquire和release,獲取許可時(shí),如果沒有就阻塞,如果有就立即返回

5 區(qū)別

用表格看比較方便點(diǎn)

區(qū)別CountDownLatchCyclicBarrierSemaphore
可使用次數(shù)單次多次(循環(huán)使用)多次(循環(huán)使用)
線程的阻塞阻塞單個(gè)(多個(gè))線程,以等待其他線程的執(zhí)行多個(gè)線程之間的相互阻塞超過許可數(shù),會(huì)阻塞
場景1. 計(jì)數(shù)器<br />2. 統(tǒng)計(jì)任務(wù)執(zhí)行時(shí)長<br />3. 多人對戰(zhàn)游戲的開局等待1. 大事化小,再合并<br />2. 多人對戰(zhàn)游戲的團(tuán)戰(zhàn)1. 數(shù)據(jù)庫連接池

可以看到,倒計(jì)數(shù)器主要是用來表示單個(gè)線程等待多個(gè)線程,而循環(huán)柵欄主要是用來表示多個(gè)線程之間的相互等待

總結(jié)

  1. 什么是并發(fā)工具:并發(fā)工具是一組工具類,主要是用來控制線程的執(zhí)行流程,比如阻塞某個(gè)線程,以等待其他線程

  2. 倒計(jì)數(shù)器 CountDownLatch:用來表示阻塞某個(gè)(某些)線程,以等待其他多個(gè)線程的任務(wù)執(zhí)行完成

  3. 循環(huán)柵欄 CyclicBarrier:用來表示多個(gè)線程之間的相互等待協(xié)作(阻塞)

  4. 信號(hào)量 Semaphore:用來表示允許同時(shí)訪問指定資源的許可數(shù)(線程數(shù))

  5. 區(qū)別:

區(qū)別CountDownLatchCyclicBarrierSemaphore
可使用次數(shù)單次多次(循環(huán)使用)多次(循環(huán)使用)
線程的阻塞阻塞單個(gè)(多個(gè))線程,以等待其他線程的執(zhí)行多個(gè)線程之間的相互阻塞超過許可數(shù),會(huì)阻塞
場景1. 計(jì)數(shù)器<br />2. 統(tǒng)計(jì)任務(wù)執(zhí)行時(shí)長<br />3. 多人對戰(zhàn)游戲的開局等待1. 大事化小,再合并<br />2. 多人對戰(zhàn)游戲的團(tuán)戰(zhàn)1. 數(shù)據(jù)庫連接池

關(guān)于“Java并發(fā)工具怎么用”這篇文章就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,使各位可以學(xué)到更多知識(shí),如果覺得文章不錯(cuò),請把它分享出去讓更多的人看到。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI