Java併發編程—併發工具類

併發編程—併發工具類

這裏將會記錄一些併發工具類:CountDownLatch、CyclicBarrier、Semaphore

CountDownLatch

CountDownLatch是什麼

CountDownLatch是在java1.5被引入的,存在於java.util.concurrent包下。它的作用是當一個線程任務完成後,它必須等待其它的線程的任務執行完成後,主線程才能繼續往下執行。
CountDownLatch是通過一個計數器來實現的,計數器的初始值爲線程的數量。每當一個線程完成了自己的任務後,計數器的值就會減1(這個可以根據業務去確定,也就是不一定就是減1)。當計數器值到達0時,它表示所有的線程已經完成了任務,然後在閉鎖上等待的線程就可以恢復執行任務。
在這裏插入圖片描述

CountDownLatch如何使用

方法說明:

  • new CountDownLatch(6):我們實例化CountDownLatch時,需要傳入一個參數[int]count,這個count就是計數器的數值。
  • countDown():做減一的操作,每調用一次,計數器的數值就會減1。
  • await():使線程阻塞,等待計數器的數值變成0時,放行阻塞的線程。

具體使用:

    /**
     * 定義
     */
    private static CountDownLatch downLatch = new CountDownLatch(6);
    /**
     * 扣減的線程
     */
    static class DownLatchThread implements Runnable {

        @Override
        public void run() {
        	// 業務代碼
         
            // countDown 可以理解爲扣減1也就是 -1
            downLatch.countDown();
            System.out.println("Thread: " + Thread.currentThread().getId());
        }
    }

    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 6; i++) {
            // 啓動六個線程 也就是說 countDown運行了六次 【-6】
            new Thread(new DownLatchThread()).start();
        }
        // await(), 等待6個線程全部執行完
        downLatch.await();

        // 打印 可以看出這句話是六個線程運行完成之後才運行的
        System.out.println("主線程運行");
    }

運行的結果:

Thread: 11
Thread: 12
Thread: 13
Thread: 14
Thread: 15
Thread: 16
主線程運行

從上面的代碼來看我們初始化了CountDownLatch的大小爲6,然後啓動6個線程,這六個線程都調用一次countDown()方法。從結果來看,主線程是等待6個線程運行完之後才執行的,這也體現了CountDownLatch的作用。【如果我啓動五個線程,也就是調用五次countDown()方法,這時線程會一直阻塞。】

CyclicBarrier

CyclicBarrier是什麼

從字面意思上來看CyclicBarrier就是循環(Cyclic)的屏障(Barrier);
作用:讓一組線程達到某個屏障,被阻塞,一直到組內最後一個線程達到屏障時,屏障開放,所有被阻塞的線程會繼續運行。

CyclicBarrier如何使用

方法說明

  • new CyclicBarrier(6):實例化CyclicBarrier時傳入參數count,count可以理解爲線程的數量
  • new CyclicBarrier(int count, Runnable runnable):當最後一個一組中的最後一個線程任務執行完後,指定一個任務執行
  • await():調用的線程阻塞,直到一組線程中的最後一個線程調用await(),所有被awati()方法阻塞的線程都會被放行
  • reset():將屏障重置爲其初始狀態。如果所有參與者目前都在屏障處等待,則它們將返回,同時拋出一個BrokenBarrierException。

具體使用:

    /**
     * 實例化CyclicBarrier
     */
    private static CyclicBarrier cyclicBarrier = new CyclicBarrier(6);

    /**
     * 工作線程
     */
    static class BarrierThread implements Runnable {

        @Override
        public void run() {
            try {
                // 不是最後一個線程,阻塞
                System.out.println("thread: "+ Thread.currentThread().getId() + " await.");
                cyclicBarrier.await();
                // 只有當最後一個線程的調用await之後,每個線程await()下面的代碼纔會執行
                System.out.println("do something .");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        for (int i = 0; i < 6; i++) {
            new Thread(new BarrierThread()).start();
        }
        System.out.println("主線程運行");
    }

運行結果

主線程運行
thread: 13 await.
thread: 11 await.
thread: 12 await.
thread: 14 await.
thread: 15 await.
thread: 16 await.
do something .
do something .
do something .
do something .
do something .
do something .

這段代碼也很簡單,實例化一個大小爲6的CyclicBarrier,啓動6個線程每個線程都調用了await()方法。從打印的結果來看,當線程16運行之前,線程11,12,13,14,15都是阻塞狀態,線程16 執行了await()方法後。所有的線程都被放行,繼續執行await()方法後的代碼。

CyclicBarrier與CountDownLatch比較
  • CountDownLatch:一個線程(或者多個),等待另外N個線程完成某個事情之後才能執行;CyclicBarrier:N個線程相互等待,任何一個線程完成之前,所有的線程都必須等待。
  • CountDownLatch:一次性的;CyclicBarrier:可以重複使用。
  • CountDownLatch基於AQS;CyclicBarrier基於鎖和Condition。本質上都是依賴於volatile和CAS實現的。

Semaphore

Semaphore是什麼

字面意思:信號量
作用:控制同時訪問某個特定資源的線程數量,用在流量控制

Semaphore如何使用

重要方法說明

  • new Semaphore(int permits):初始化時,傳入許可證的數量;
  • acquire():獲取許可證, -1;
  • release():歸還許可證, +1;
  • hasQueuedThreads():是否有阻塞的線程;
  • getQueueLength():得到阻塞線程的數量;
  • availablePermits():可用許可證的數量;

具體使用

    /**
     * 座位數量10
     */
    private final Semaphore seatCount = new Semaphore(10);

    /**
     * 構造方式私有化
     */
    private UseSemaphoreDemo() {
    }

    private static UseSemaphoreDemo instance = null;

    private static UseSemaphoreDemo getInstance() {
        if (instance == null) {
            synchronized (UseSemaphoreDemo.class) {
                instance = new UseSemaphoreDemo();
            }
        }
        return instance;
    }

    static class WorkThread extends Thread {
        UseSemaphoreDemo semaphoreDemo = UseSemaphoreDemo.getInstance();

        @Override
        public void run() {
            try {
                // 獲取座位使用權 就是調用 acquire() 方法
                semaphoreDemo.seatCount.acquire();
                System.out.println("線程Thread: " + Thread.currentThread().getId() +
                        "獲取到使用權");
                if (semaphoreDemo.seatCount.hasQueuedThreads()) {
                    // 是否有等待的線程
                    System.out.println("等待的線程數量:" + semaphoreDemo.seatCount.getQueueLength());
                }
                // 獲取使用權後休眠
                Thread.sleep(500 + Thread.currentThread().getId());
                // 歸還座位的使用權 調用release()方法
                semaphoreDemo.seatCount.release();
                System.out.println("線程Thread: " + Thread.currentThread().getId() +
                        "歸還了使用權");
                System.out.println("可用座位:" + semaphoreDemo.seatCount.availablePermits());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        // 60個線程去獲取座位
        for (int i = 0; i < 60; i++) {
            new WorkThread().start();
        }
    }

上面代碼的運行結果

線程Thread: 11獲取到使用權
線程Thread: 12獲取到使用權
線程Thread: 13獲取到使用權
線程Thread: 14獲取到使用權
線程Thread: 15獲取到使用權
線程Thread: 16獲取到使用權
線程Thread: 17獲取到使用權
線程Thread: 18獲取到使用權
線程Thread: 19獲取到使用權
線程Thread: 20獲取到使用權
線程Thread: 11歸還了使用權
線程Thread: 21獲取到使用權
可用座位:0
等待的線程數量:49
線程Thread: 12歸還了使用權
線程Thread: 22獲取到使用權
可用座位:0
等待的線程數量:48
線程Thread: 13歸還了使用權
線程Thread: 23獲取到使用權
...

上面代碼的意思:一共10個座位,60個線程去獲取使用權。
從控制檯打印的結果來看:前10個線程迅速的拿到了座位的使用權,而之後的線程必須要等到拿到使用權的線程歸還使用權後才能得到。這個也說明了Semaphore的作用。

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