了解jdk中協(xié)調(diào)線程的工具類

JDK當(dāng)中除了以上的ReentrantLock和Condition之外,還有很多幫助猿友們協(xié)調(diào)線程的工具類。
CountDownLatch(計(jì)數(shù)器閉鎖) 簡單記憶:小明被老師罰站(一個線程await),要等所有同學(xué)走后(設(shè)置的參數(shù)被其他線程countdown()到0)小明才能回家(執(zhí)行await()之后的代碼)。

CyclicBarrier(可循環(huán)使用的屏障)簡單記憶:一場規(guī)定好人數(shù)的百米田徑比賽(new CyclicBarrier(int n)),要等所有人都在起跑線上準(zhǔn)備好(所有線程阻塞在await()方法)之后,才一起出發(fā)(執(zhí)行await()之后的代碼)

Semaphore簡單記憶:廁所里3個坑,10個人一塊來上廁所,每次只能進(jìn)3個,出來一個(release()),再進(jìn)一個(acquire()),想起了一個謎語:“大家排隊(duì)上廁所,打一個城市名?”
Exchanger(交換器)簡單記憶:黑社會a和黑社會b相約到廢棄倉庫交易(final Exchanger<String> exchanger = new Exchanger<String>();),不見不散,誰先到了就等另一人,兩人都到后,a一手交錢exchanger.exchange("錢"),b一手交貨 (exchanger.exchange("貨")),結(jié)果是a得到貨,b得到錢(exchange("")返回值)。

1,CountDownLatch(計(jì)數(shù)器閉鎖)

這個類是為了幫助猿友們方便的實(shí)現(xiàn)一個這樣的場景,就是某一個線程需要等待其它若干個線程完成某件事以后才能繼續(xù)進(jìn)行。比如下面的這個程序

package concurrent;

import java.util.concurrent.CountDownLatch;

/**
 * @author zuoxiaolong
 *
 */
public class CountDownLatchTest {

    public static void main(String[] args) throws InterruptedException {
       //只有一個構(gòu)造器,參數(shù)是
        final CountDownLatch countDownLatch = new CountDownLatch(10);
        for (int i = 0; i < 10; i++) {
            final int number = i + 1;
            Runnable runnable = new Runnable() {
                public void run() {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {}
                    System.out.println("執(zhí)行任務(wù)[" + number + "]");
                    countDownLatch.countDown();
                    System.out.println("完成任務(wù)[" + number + "]");
                }
            };
            Thread thread = new Thread(runnable);
            thread.start();
        }
        System.out.println("主線程開始等待...");
        countDownLatch.await();
        System.out.println("主線程執(zhí)行完畢...");
    }
}

這個程序的主線程會等待CountDownLatch進(jìn)行10次countDown方法的調(diào)用才會繼續(xù)執(zhí)行。我們可以從打印的結(jié)果上看出來,盡管有的時候完成任務(wù)的打印會出現(xiàn)在主線程執(zhí)行完畢之后,但這只是因?yàn)閏ountDown已經(jīng)執(zhí)行完畢,主線程的打印語句先一步執(zhí)行而已。

分析一下這個CountDownLatch這個類,CountDownLatch是一個計(jì)數(shù)器閉鎖,主要的功能就是通過await()方法來阻塞住當(dāng)前線程,然后等待計(jì)數(shù)器減少到0了,再喚起這些線程繼續(xù)執(zhí)行。 這個類里主要有兩個方法,一個是向下減計(jì)數(shù)器的方法:countdown(),其實(shí)現(xiàn)的核心代碼如下:

public boolean tryReleaseShared(int releases) {  
    // Decrement count; signal when transition to zero  
    for (;;) {   
    int c = getState();     
    if (c == 0)    
        return false;     
    int nextc = c-1;    
    if (compareAndSetState(c, nextc))    
        return nextc == 0;    
    }    
}   

很簡單,如果取得當(dāng)前的狀態(tài)為0,說明這個鎖已經(jīng)結(jié)束,直接返回false;如果沒有結(jié)束,然后去設(shè)置計(jì)數(shù)器減1,如果compareAndSetState不成功,則繼續(xù)循環(huán)執(zhí)行。 而其中的一直等待計(jì)數(shù)器歸零的方法是await()。


2,CyclicBarrier
  這個類是為了幫助猿友們方便的實(shí)現(xiàn)多個線程一起啟動的場景,就像賽跑一樣,只要大家都準(zhǔn)備好了,那就開始一起沖。比如下面這個程序,所有的線程都準(zhǔn)備好了,才會一起開始執(zhí)行。

package concurrent;

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

/**
 * @author zuoxiaolong
 *
 */
public class CyclicBarrierTest {

    public static void main(String[] args) {
        final CyclicBarrier cyclicBarrier = new CyclicBarrier(10);
        for (int i = 0; i < 10; i++) {
            final int number = i + 1;
            Runnable runnable = new Runnable() {
                public void run() {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {}
                    System.out.println("等待執(zhí)行任務(wù)[" + number + "]");
                    try {
                        cyclicBarrier.await();
                    } catch (InterruptedException e) {
                    } catch (BrokenBarrierException e) {
                    }
                    System.out.println("開始執(zhí)行任務(wù)[" + number + "]");
                }
            };
            Thread thread = new Thread(runnable);
            thread.start();
        }
    }
    
}

3,Semaphore(旗語)
  這個類是為了幫助猿友們方便的實(shí)現(xiàn)控制數(shù)量的場景,可以是線程數(shù)量或者任務(wù)數(shù)量等等。來看看下面這段簡單的代碼。

package concurrent;

import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author zuoxiaolong
 *
 */
public class SemaphoreTest {

    public static void main(String[] args) throws InterruptedException {
        final Semaphore semaphore = new Semaphore(10);
        final AtomicInteger number = new AtomicInteger();
        for (int i = 0; i < 100; i++) {
            Runnable runnable = new Runnable() {
                public void run() {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {}
                    try {
                        semaphore.acquire();
                        number.incrementAndGet();
                    } catch (InterruptedException e) {}
                }
            };
            Thread thread = new Thread(runnable);
            thread.start();
        }
        Thread.sleep(10000);
        System.out.println("共" + number.get() + "個線程獲得到信號");
        System.exit(0);
    }
    
}

4,Exchanger
 這個類是為了幫助猿友們方便的實(shí)現(xiàn)兩個線程交換數(shù)據(jù)的場景,使用起來非常簡單,看看下面這段代碼。

package concurrent;

import java.util.concurrent.Exchanger;

/**
 * @author zuoxiaolong
 *
 */
public class ExchangerTest {

    public static void main(String[] args) throws InterruptedException {
        final Exchanger<String> exchanger = new Exchanger<String>();
        Thread thread1 = new Thread(new Runnable() {
            public void run() {
                try {
                    System.out.println("線程1等待接受");
                    String content = exchanger.exchange("thread1");
                    System.out.println("線程1收到的為:" + content);
                } catch (InterruptedException e) {}
            }
        });
        Thread thread2 = new Thread(new Runnable() {
            public void run() {
                try {
                    System.out.println("線程2等待接受并沉睡3秒");
                    Thread.sleep(3000);
                    String content = exchanger.exchange("thread2");
                    System.out.println("線程2收到的為:" + content);
                } catch (InterruptedException e) {}
            }
        });
        thread1.start();
        thread2.start();
    }
    
}

兩個線程在只有一個線程調(diào)用exchange方法的時候調(diào)用方會被掛起,當(dāng)都調(diào)用完畢時,雙方會交換數(shù)據(jù)。在任何一方?jīng)]調(diào)用exchange之前,線程都會處于掛起狀態(tài)。

參考 沒聽說過這些,就不要說你懂并發(fā)了,three。

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

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

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