java并發(fā)包工具之CountDownLatch/CyclicBarrier/Semaphore

幾天前我在思考這樣一個(gè)問題,如果用java的多線程來做一個(gè)合力完成的事,例如合并請求,假設(shè)前端來了3個(gè)請求,到j(luò)ava這一層需要合成一個(gè)請求并返回,那么需要啟用3個(gè)線程去請求,并等所有的結(jié)果都返回了再合并一下返回給前端,這該如何去做?

最開始我找到的是Thread.join這個(gè)方法。

Thread.join這個(gè)方法在源碼中的描述是

Waits for this thread to die.

意思是等這個(gè)線程執(zhí)行完了再去執(zhí)行主線程。
那么我們針對上述問題可以實(shí)現(xiàn)一個(gè)這樣的版本

package thread;

/**
 * @author lkxiaolou
 */
public class ThreadPrint implements Runnable {

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + " is running");
        try {
            Thread.sleep(5000L);
        } catch (Exception e) {
        }
    }

    public static void main(String[] args) {
        Thread thread1 = new Thread(new ThreadPrint());
        Thread thread2 = new Thread(new ThreadPrint());
        Thread thread3 = new Thread(new ThreadPrint());
        thread1.start();
        thread2.start();
        try {
            // 等待子線程完成后再執(zhí)行主線程
            thread1.join();
            thread2.join();
        } catch (Exception e) {

        }
        thread3.start();
    }
}

先啟動(dòng)線程1和2,再等1、2執(zhí)行完再啟動(dòng)第3個(gè)線程。
運(yùn)行結(jié)果是這樣的

Thread-0 is running
Thread-1 is running
Thread-1 done
Thread-0 done
Thread-2 is running
Thread-2 done

線程0和1不一定誰先執(zhí)行完,但一定是在線程2執(zhí)行前執(zhí)行完成。

后來我又發(fā)現(xiàn)了java對多線程提供了一組并發(fā)包來做這些事情,其中有個(gè)叫CountDownLatch的類。這個(gè)類用起來簡單也更加好理解,CountDownLatch提供了一個(gè)計(jì)數(shù)器,每當(dāng)一個(gè)線程執(zhí)行完計(jì)數(shù)器減1,直到減成0,主線程才可以開始執(zhí)行。

package thread;

import java.util.concurrent.CountDownLatch;

/**
 * @author lkxiaolou
 */
public class ThreadPrintCountDownLatch implements Runnable {

    public static CountDownLatch countDownLatch = new CountDownLatch(2);

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + " is running");
        try {
            Thread.sleep(1000L);
        } catch (Exception e) {

        }
        System.out.println(Thread.currentThread().getName() + " done");
        countDownLatch.countDown();
    }

    public static void main(String[] args) {
        Thread thread1 = new Thread(new ThreadPrintCountDownLatch());
        Thread thread2 = new Thread(new ThreadPrintCountDownLatch());
        Thread thread3 = new Thread(new ThreadPrintCountDownLatch());
        thread1.start();
        thread2.start();
        try {
            countDownLatch.await();
        } catch (Exception e) {

        }
        thread3.start();
    }
}

輸出也和上面一樣

Thread-0 is running
Thread-1 is running
Thread-0 done
Thread-1 done
Thread-2 is running
Thread-2 done

要注意減1的操作 countDownLatch.countDown(); 一定要放在關(guān)鍵部分最后,關(guān)鍵部分是你想等的操作已經(jīng)完成了才行。countDownLatch有個(gè)缺點(diǎn)是計(jì)數(shù)器只能減不能加,一旦減到了0就不能再用了。

看了countDownLatch之后又發(fā)現(xiàn)了兩個(gè)類似的類:CyclicBarrier和Semaphore。
CyclicBarrier,通過它可以實(shí)現(xiàn)讓一組線程等待至某個(gè)狀態(tài)之后再全部同時(shí)執(zhí)行。

package thread;

import java.util.concurrent.CyclicBarrier;

/**
 * @author lkxiaolou
 */
public class ThreadPrintCyclicBarrier implements Runnable {

    public ThreadPrintCyclicBarrier(CyclicBarrier cyclicBarrier) {
        this.cyclicBarrier = cyclicBarrier;
    }

    private CyclicBarrier cyclicBarrier;

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + " is running");
        try {
            Thread.sleep(1000L);
        } catch (Exception e) {

        }
        try {
            cyclicBarrier.await();
        } catch (Exception e) {

        }
        System.out.println(Thread.currentThread().getName() + " done");
    }

    public static void main(String[] args) {

        CyclicBarrier cyclicBarrier = new CyclicBarrier(2, new ThreadPrint()); // 這個(gè)線程輸出帶括號

        Thread thread1 = new Thread(new ThreadPrintCyclicBarrier(cyclicBarrier));
        Thread thread2 = new Thread(new ThreadPrintCyclicBarrier(cyclicBarrier));
        thread1.start();
        thread2.start();
    }
}

輸出是這樣的

Thread-0 is running
Thread-1 is running
[Thread-0] is running
[Thread-0] done
Thread-0 done
Thread-1 done

可以看出CyclicBarrier可以讓所有線程先執(zhí)行,執(zhí)行到cyclicBarrier.await再等待,等所有線程都執(zhí)行到cyclicBarrier.await時(shí)執(zhí)行我們指定的線程(也可以不指定),指定線程執(zhí)行完了再把之前暫停的線程都起起來同時(shí)執(zhí)行。似乎能做的事情更多。

最后我們看一下 Semaphore類,這個(gè)翻譯成信號量,他能控制對臨界資源的訪問。

假若一個(gè)工廠有5臺機(jī)器,但是有8個(gè)工人,一臺機(jī)器同時(shí)只能被一個(gè)工人使用,只有使用完了,其他工人才能繼續(xù)使用。那么我們就可以通過Semaphore來實(shí)現(xiàn)。

package thread;

import java.util.concurrent.Semaphore;

/**
 * @author lkxiaolou
 */
public class ThreadPrintSemaphore implements Runnable {

    public static Semaphore semaphore = new Semaphore(2);

    @Override
    public void run() {

        try {
            semaphore.acquire();
        } catch (Exception e) {

        }

        System.out.println(Thread.currentThread().getName() + " is running");
        try {
            Thread.sleep(1000L);
        } catch (Exception e) {

        }
        try {
            semaphore.release();
        } catch (Exception e) {

        }
        System.out.println(Thread.currentThread().getName() + " done");
    }

    public static void main(String[] args) {
        
        Thread thread1 = new Thread(new ThreadPrintSemaphore());
        Thread thread2 = new Thread(new ThreadPrintSemaphore());
        Thread thread3 = new Thread(new ThreadPrintSemaphore());
        thread1.start();
        thread2.start();
        thread3.start();
    }
}

輸出結(jié)果:

Thread-0 is running
Thread-1 is running
Thread-1 done
Thread-2 is running
Thread-0 done
Thread-2 done

多次運(yùn)行可以看出0和1線程必須有一個(gè)釋放了,2才能開始運(yùn)行。

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

相關(guān)閱讀更多精彩內(nèi)容

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