同步工具:CountDownLatch、CyclicBarrier和Semaphore

1. CountDownLatch

1.1 功能及使用場景

一個同步工具,使得一個或多個線程等待一組線程執行完成後再執行。dom

使用場景:等待一些前置任務執行完成後,再執行特定的功能。好比,系統啓動時,各類配置生效後,才能運行提供服務。ide

1.2 代碼實例

public class CountDownLatchTest {

    public static void main(String[] args) {
        final CountDownLatch latch = new CountDownLatch(5);

        for (int i = 0; i < 5; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        TimeUnit.SECONDS.sleep(new Random().nextInt(10));
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + " end, " + System.currentTimeMillis());
                    latch.countDown();
                }
            }).start();
        }

        try {
            System.out.println("main latch.await() " + System.currentTimeMillis());
            latch.await();
            System.out.println("main latch.await() end " + System.currentTimeMillis());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

}

特別注意:CountDownLatch初始化時,數量必定要等於等待的線程的數量。工具

2. CyclicBarrier

2.1 功能及使用場景

CyclicBarrier(可循環同步屏障),控制一組線程相互等待,直到全部線程都到達屏障點。全部線程都到達屏障點後,同時開始執行剩餘的任務。ui

可循環,意味着當全部線程都到達屏障後,屏障能夠被再次重複利用。線程

使用場景

多個任務同時到達某個臨界狀況時,才能同時執行剩餘任務,不然相互等待。code

2.2 示例代碼

public class CyclicBarrierTest {

    public static void main(String[] args) {
        int num = 3;
        final CyclicBarrier barrier = new CyclicBarrier(num);
        for (int i = 0; i < num; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        TimeUnit.SECONDS.sleep(new Random().nextInt(5));
                        System.out.println(Thread.currentThread().getName() + "執行結束,開始等待其它線程, " + System.currentTimeMillis());
                        barrier.await();
                        System.out.println(Thread.currentThread().getName() + "全部線程等待都執行完成,開始執行剩下任務, " + System.currentTimeMillis());
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } catch (BrokenBarrierException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }

        // 屏障能夠在上一次使用完成後被再次使用
        for (int i = 0; i < num; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        TimeUnit.SECONDS.sleep(new Random().nextInt(5));
                        System.out.println(Thread.currentThread().getName() + "執行結束,開始等待其它線程, " + System.currentTimeMillis());
                        barrier.await();
                        System.out.println(Thread.currentThread().getName() + "全部線程等待都執行完成,開始執行剩下任務, " + System.currentTimeMillis());
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } catch (BrokenBarrierException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }
        System.out.println("main end at " + System.currentTimeMillis());
    }

}

3. Semaphore

3.1 功能及使用場景

Semaphore(信號量),一個計數信號量。概念上,一個信號量維持了一個許可集合。對象

  • acquire()方法:獲取一個許可,若是沒有獲取到許可,則一直阻塞到得到一個許可;
  • release()方法:歸還一個許可。

實際上,並無真正的許可對象,只是計數。資源

使用場景

限制資源同時被訪問的線程數量get

3.2 使用代碼

public class SemaphoreTest {

    public static void main(String[] args) {
        final Semaphore semaphore = new Semaphore(3);
        for (int i = 0; i < 10; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        System.out.println(Thread.currentThread().getName() + "嘗試獲取許可, " + System.currentTimeMillis());
                        semaphore.acquire();
                        System.out.println(Thread.currentThread().getName() + "獲取許可, " + System.currentTimeMillis());
                        TimeUnit.SECONDS.sleep(new Random().nextInt(5));
                        System.out.println(Thread.currentThread().getName() + "執行結束, " + System.currentTimeMillis());
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        semaphore.release();
                    }
                }
            }).start();
        }
        System.out.println("main end! " + System.currentTimeMillis());
    }

}

特別注意:得到許可並執行完邏輯後,必定要釋放,不然許可不會被歸還。(有借有還,再借不難)同步

4. 總結

  • CountDownLatch:一個任務等待前置任務執行完後才能執行
  • CyclicBarrier: 一組任務相互等待,直到全部線程均到達某個臨界狀態
  • Semaphore: 一組許可,控制資源被同時訪問的數量(獲取許可,也要歸還許可)
相關文章
相關標籤/搜索