併發編程中四個神奇的工具類

CountDownLatch - 門閂

四個人相約出去上網,你告訴司機,等四個都上車了,您在開。所以每個人上車的時候,都會告訴司機,你來了。司機就會記錄下來,當來的人夠四個的時候,自己才能開車去往目的地。

它常用的兩個方法

countDown() // 給司機報到的方法
await() // 示意開車的方法 (就是四個都到了,才執行 此方法後邊的代碼,否則線程都阻塞)

CountDownLatch 的使用方法與上邊的思想比較類似,就是它初始化的時候會指定一個數值,當線程執行滿意之後,就可以呼喚countDown()它一次,當呼喚到達指定次數之後,就會執行 await() 後的方法

demo

@Test
public void countDownLatchTest() throws InterruptedException {
    // 定義一個司機,來四個人才能發車
    CountDownLatch countDownLatch = new CountDownLatch(4);
    // 定義四個線程去解決它
    IntStream.range(0, 4).forEach(number -> {
        new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + number + "到了");
            // 嘗試開鎖
            countDownLatch.countDown();
        }, "線程" + number).start();

    });
    countDownLatch.await();
    System.out.println("開車了");
}

輸出結果
在這裏插入圖片描述

但是,它有個不足,就只能使用一次,如果下次想再次使用,需要重新定義一個門閂,所以引出了下一個可以複用的CyclicBarrier


CyclicBarrier - 籬柵

它的思想和CountDownLatch 類似,只不過它可以重複使用

demo

@Test
public void cyclicBarrierTest() throws InterruptedException {
	// 定義一個規則,出租車坐滿四個人就發車
    CyclicBarrier cyclicBarrier = 
    		new CyclicBarrier(4, () -> System.out.println("4個乘客到位,發車"));
    Stream.iterate(0, n -> n+1).limit(10).forEach(n -> {
    	// 睡一秒,保證程序的執行順序
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + "乘客已就位");
            try {
            	// 通知一下司機,讓自己看一下,能不能發車了
                cyclicBarrier.await();
            } catch (InterruptedException | BrokenBarrierException e) {
                e.printStackTrace();
            }
        },n.toString()).start();
    });
    // 睡一下,防止程序執行太快看不到結果
    TimeUnit.SECONDS.sleep(10);
}

輸出結果
在這裏插入圖片描述


Exchanger - 交換機

Exchanger 的機制:就是兩個線程需要交換信息,我給了你信息,你再給我信息,咱倆才能往下走;我給了你,你不給我,我就一直阻塞着(如下圖)。
在這裏插入圖片描述
demo

@Test
public void workmateTest() throws InterruptedException {
	// 定義一個交換機
    Exchanger<String> workmate = new Exchanger<>();
    new Thread(() -> {
        try {
        	System.out.println();
        	// 用我的名字,換取你的名字
            String expressDocument = workmate.exchange("01");
            // 輸出換到的名字
            System.out.println
            	(Thread.currentThread().getName()+"換到的名字:"+ expressDocument);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }, "thread-01").start();
    new Thread(() -> {
        try {
        	// 用我的名字,換取你的名字
            String expressDocument = workmate.exchange("02");
            // 輸出換到的名字
            System.out.printlnSystem.out.println
            	(Thread.currentThread().getName()+"換到的名字:"+ expressDocument);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }, "thread-02").start();
    // 讓主線程睡一會,不然程序直接結束了,看不到效果
    Thread.sleep(5000);
}

輸出結果
在這裏插入圖片描述
上邊描述的兩個人交換的情況,但是三個人的怎麼個情況呢?

直接告訴大家結果吧:就是 隨機 兩個交換,另外一個什麼都換不到,自閉了(阻塞),多個線程也是如此。可以理解爲它只能進行兩個線程之間的數據交換。


Semaphore - 信號量

它會定義一個資源的數量,當線程申請資源的時候,數量就會減少,當數量減少爲0 的時候,其他線程再申請資源的時候,就需要等待別的線程把資源釋放的時候,等待的線程才能獲取資源。

這個過程和進停車廠停車特別類似,有空閒車位,保安會讓你進;但是沒有空閒車位,你只能等其他人出來之後,你在進去了。

小demo

@Test
public void semaphoreTest() {
	// 定義5個信號量
	Semaphore semaphore = new Semaphore(5);
	// 用於生成隨機數
    Random random = new Random();
    IntStream.range(0, 15).forEach(n -> {
        new Thread(() -> {
            try {
            	// 佔用資源
                semaphore.acquire();
                System.out.println("佔用資源" + Thread.currentThread().getName());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            // 隨機生成資源佔用的時間
            int parkingTime = random.nextInt(10) + 2;
            try {
                TimeUnit.SECONDS.sleep(parkingTime);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            // 釋放資源
            semaphore.release();
            System.out.println("釋放資源" + Thread.currentThread().getName());
        }, "線程" + n).start();
    });
    // 讓主線程睡一會,不然程序直接結束了,看不到效果
    try {
        Thread.sleep(50000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

輸出結果
在這裏插入圖片描述

簡做總結,如有不足,歡迎指出。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章