Java多線程打輔助的三個小夥子

前言

以前學多線程的時候沒有學習線程的同步工具類(輔助類)。ps:當時以爲暫時用不上,認爲是挺高深的知識點就沒去管了..html

在前幾天,朋友發了一篇比較好的Semaphore文章過來,而後在瀏覽博客的時候又發現面試還會考,那仍是挺重要的知識點。因而花了點時間去了解一下。java

Java爲咱們提供了三個同步工具類面試

  • CountDownLatch(閉鎖)
  • CyclicBarrier(柵欄)
  • Semaphore(信號量)

這幾個工具類其實說白了就是爲了可以更好控制線程之間的通信問題~編程

1、CountDownLatch

1.1CountDownLatch簡介

  • A synchronization aid that allows one or more threads to wait until a set of operations being performed in other threads completes.

簡單來講:CountDownLatch是一個同步的輔助類,容許一個或多個線程一直等待直到其它線程完成它們的操做。微信

它經常使用的API其實就兩個:await()countDown()多線程

使用說明:併發

  • count初始化CountDownLatch,而後須要等待的線程調用await方法。await方法會一直受阻塞直到count=0。而其它線程完成本身的操做後,調用countDown()使計數器count減1。當count減到0時,全部在等待的線程均會被釋放
  • 說白了就是經過count變量來控制等待,若是count值爲0了(其餘線程的任務都完成了),那就能夠繼續執行。

1.2CountDownLatch例子

例子:3y如今去作實習生了,其餘的員工還沒下班,3y很差意思先走,等其餘的員工都走光了,3y再走。ide

import java.util.concurrent.CountDownLatch;

public class Test {

    public static void main(String[] args) {

        final CountDownLatch countDownLatch = new CountDownLatch(5);

		System.out.println("如今6點下班了.....");

        // 3y線程啓動
        new Thread(new Runnable() {
            @Override
            public void run() {
           
                try {
                    // 這裏調用的是await()不是wait()
                    countDownLatch.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("...其餘的5個員工走光了,3y終於能夠走了");
            }
        }).start();

        // 其餘員工線程啓動
        for (int i = 0; i < 5; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println("員工xxxx下班了");
                    countDownLatch.countDown();
                }
            }).start();
        }
    }
}

複製代碼

輸出結果:工具

再寫個例子:3y如今負責倉庫模塊功能,可是能力太差了,寫得很慢,別的員工都須要等3y寫好了才能繼續往下寫。post

import java.util.concurrent.CountDownLatch;

public class Test {

    public static void main(String[] args) {

        final CountDownLatch countDownLatch = new CountDownLatch(1);

        // 3y線程啓動
        new Thread(new Runnable() {
            @Override
            public void run() {

                try {
                    Thread.sleep(5);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("3y終於寫完了");
                countDownLatch.countDown();

            }
        }).start();

        // 其餘員工線程啓動
        for (int i = 0; i < 5; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println("其餘員工須要等待3y");
                    try {
                        countDownLatch.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("3y終於寫完了,其餘員工能夠開始了!");
                }
            }).start();
        }
    }
}
複製代碼

輸出結果:

參考資料:

2、CyclicBarrier

2.1CyclicBarrier簡介

  • A synchronization aid that allows a set of threads to all wait for each other to reach a common barrier point. CyclicBarriers are useful in programs involving a fixed sized party of threads that must occasionally wait for each other. The barrier is called cyclic because it can be re-used after the waiting threads are released.

簡單來講:CyclicBarrier容許一組線程互相等待,直到到達某個公共屏障點。叫作cyclic是由於當全部等待線程都被釋放之後,CyclicBarrier能夠被重用(對比於CountDownLatch是不能重用的)

使用說明:

  • CountDownLatch注重的是等待其餘線程完成,CyclicBarrier注重的是:當線程到達某個狀態後,暫停下來等待其餘線程,全部線程均到達之後,繼續執行。

2.2CyclicBarrier例子

例子:3y和女友約了去廣州夜上海吃東西,因爲3y和3y女友住的地方不一樣,天然去的路徑也就不同了。因而他倆約定在體育西路地鐵站集合,約定等到相互見面的時候就發一條朋友圈。

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

public class Test {

    public static void main(String[] args) {

        final CyclicBarrier CyclicBarrier = new CyclicBarrier(2);
        for (int i = 0; i < 2; i++) {

            new Thread(() -> {

                String name = Thread.currentThread().getName();
                if (name.equals("Thread-0")) {
                    name = "3y";
                } else {
                    name = "女友";
                }
                System.out.println(name + "到了體育西");
                try {

                    // 兩我的都要到體育西才能發朋友圈
                    CyclicBarrier.await();
                    // 他倆到達了體育西,看見了對方發了一條朋友圈:
                    System.out.println("跟" + name + "去夜上海吃東西~");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}
複製代碼

測試結果:

玩了一天之後,各自回到家裏,3y和女友約定各自洗澡完以後再聊天

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

public class Test {

    public static void main(String[] args) {

        final CyclicBarrier CyclicBarrier = new CyclicBarrier(2);
        for (int i = 0; i < 2; i++) {

            new Thread(() -> {

                String name = Thread.currentThread().getName();
                if (name.equals("Thread-0")) {
                    name = "3y";
                } else {
                    name = "女友";
                }
                System.out.println(name + "到了體育西");
                try {

                    // 兩我的都要到體育西才能發朋友圈
                    CyclicBarrier.await();
                    // 他倆到達了體育西,看見了對方發了一條朋友圈:
                    System.out.println("跟" + name + "去夜上海吃東西~");

                    // 回家
                    CyclicBarrier.await();
                    System.out.println(name + "洗澡");

                    // 洗澡完以後一塊兒聊天
                    CyclicBarrier.await();

                    System.out.println("一塊兒聊天");

                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}
複製代碼

測試結果:

參考資料:

3、Semaphore

3.1Semaphore簡介

Semaphores are often used to restrict the number of threads than can access some (physical or logical) resource.


  • A counting semaphore. Conceptually, a semaphore maintains a set of permits. Each {@link #acquire} blocks if necessary until a permit is available, and then takes it. Each {@link #release} adds a permit,potentially releasing a blocking acquirer.However, no actual permit objects are used; the {@code Semaphore} just keeps a count of the number available and acts accordingly.

Semaphore(信號量)實際上就是能夠控制同時訪問的線程個數,它維護了一組**"許可證"**。

  • 當調用acquire()方法時,會消費一個許可證。若是沒有許可證了,會阻塞起來
  • 當調用release()方法時,會添加一個許可證。
  • 這些"許可證"的個數其實就是一個count變量罷了~

3.2Semaphore例子

3y女友開了一間賣酸奶的小店,小店一次只能容納5個顧客挑選購買,超過5個就須要排隊啦~~~

import java.util.concurrent.Semaphore;

public class Test {

    public static void main(String[] args) {

        // 假設有50個同時來到酸奶店門口
        int nums = 50;

        // 酸奶店只能容納10我的同時挑選酸奶
        Semaphore semaphore = new Semaphore(10);

        for (int i = 0; i < nums; i++) {
            int finalI = i;
            new Thread(() -> {
                try {
                    // 有"號"的才能進酸奶店挑選購買
                    semaphore.acquire();

                    System.out.println("顧客" + finalI + "在挑選商品,購買...");

                    // 假設挑選了xx長時間,購買了
                    Thread.sleep(1000);

                    // 歸還一個許可,後邊的就能夠進來購買了
                    System.out.println("顧客" + finalI + "購買完畢了...");
                    semaphore.release();



                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();

        }

    }
}
複製代碼

輸出結果:

反正每次只能5個客戶同時進酸奶小店購買挑選。

參考資料:

4、總結

Java爲咱們提供了三個同步工具類

  • CountDownLatch(閉鎖)
    • 某個線程等待其餘線程執行完畢後,它才執行(其餘線程等待某個線程執行完畢後,它才執行)
  • CyclicBarrier(柵欄)
    • 一組線程互相等待至某個狀態,這組線程再同時執行。
  • Semaphore(信號量)
    • 控制一組線程同時執行

本文簡單的介紹了一下這三個同步工具類是幹嗎用的,要深刻還得看源碼或者借鑑其餘的資料。

最後補充一下以前的思惟導圖知識點:

參考資料:

若是文章有錯的地方歡迎指正,你們互相交流。習慣在微信看技術文章,想要獲取更多的Java資源的同窗,能夠關注微信公衆號:Java3y

文章的目錄導航

相關文章
相關標籤/搜索