JUC ⼯具類-CountDownLatch、CyclicBarrier、Semaphore、讀寫鎖

在我前面的博客裏已經寫過了一些JUC工具類,比如Lock的RentrantLock,可以防止併發訪問異常(IllegalMonitorStateException)的CopyOnWriteArrayList、CopyOnWriteArraySet、ConcurrentHashMap。


本篇博客繼續記錄JUC工具類中常用的一些類。

1. CountDownLatch:減法計數器

可以⽤來倒計時,當兩個線程同時執⾏時,如果要確保⼀個線程優先執⾏,可以使⽤計數器,當計數器
清零的時候,再讓另⼀個線程執⾏。

public class CountDownLatchTest {
    public static void main(String[] args) {
        //JUC包提供的減法計數器
        CountDownLatch countDownLatch = new CountDownLatch(100);

        new Thread(()->{
            for (int i = 0; i < 100 ;i++){
                System.out.println(i);
                countDownLatch.countDown();
            }
        }).start();

        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        for (int i = 0; i < 30 ;i++){
            System.out.println("==========main----------");
        }

    }
}

coutDown():計數器減⼀
await():計數器停⽌,喚醒其他線程
new CountDownLatch(100)、coutDown()、await() 必須配合起來使⽤,創建對象的時候賦的值是多少,coutDown() 就必須執⾏多少次,否則計數器是沒有清零的,計數器就不會停⽌,其他線程也⽆法喚醒,所以必須保證計數器清零,coutDown() 的調⽤次數必須⼤於構造函數的參數值。

2. CyclicBarrier:加法計數器
public class CyclicBarrierTest {
    public static void main(String[] args) {
        CyclicBarrier cyclicBarrier = new CyclicBarrier(100,()->{
            System.out.println("放行");
        });

        for (int i=0; i<109; i++){
            int finalI = i;
            new Thread(()->{
                System.out.println("==>"+ finalI);
                try {
                    cyclicBarrier.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }

            }).start();
        }
    }
}

await():在其他線程中試圖喚醒計數器線程,當其他線程的執⾏次數達到計數器的臨界值時,則喚醒計數器線程,並且計數器是可以重複使⽤的,當計數器的線程執⾏完成⼀次之後,計數器⾃動清零,等待下⼀次執⾏。
new CyclicBarrier(30),for 執⾏ 90 次,則計數器的任務會執⾏ 3 次。

3. Semaphore:計數信號量

實際開發中主要使⽤它來完成限流操作,限制可以訪問某些資源的線程數量。
Semaphore 只有 3 個操作:
(1)初始化
(2)獲取許可
(3)釋放

public class SemaphoreTest {
    public static void main(String[] args) {
        //初始化
        Semaphore semaphore = new Semaphore(5);
        for (int i = 0; i < 15; i++) {
            new Thread(()->{
                //獲得許可
                try {
                    semaphore.acquire();
                    System.out.println(Thread.currentThread().getName()+"進店購物");
                            TimeUnit.SECONDS.sleep(5);
                    System.out.println(Thread.currentThread().getName()+"出店");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }finally {
                    //釋放
                    semaphore.release();
                }
            },String.valueOf(i)).start();
        }
    }
}

每個線程在執⾏的時候,⾸先需要去獲取信號量,只有獲取到資源纔可以執⾏,執⾏完畢之後需要釋放資源,留給下⼀個線程。

4.讀寫鎖

接⼝ ReadWriteLock,實現類是 ReentrantReadWriteLock,可以多線程同時讀,但是同⼀時間內只能有⼀個線程進⾏寫⼊操作。

讀寫鎖也是爲了實現線程同步,只不過粒度更細,可以分別給讀和寫的操作設置不同的鎖。

public class readWriteLockTest {
    public static void main(String[] args) {
        Cache cache = new Cache();
        for (int i=0;i<10;i++){
            final int temp = i;
            new Thread(()->{
                cache.write(temp,String.valueOf(temp));
            }).start();
        }

        for (int i=0;i<10;i++){
            final int temp = i;
            new Thread(()->{
                cache.read(temp);
            }).start();
        }
    }
}

class Cache{
    private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    private Map<Integer,String> map = new HashMap<Integer,String>();


    /**
     * 寫操作
     */
    public void write(Integer key,String val){
        readWriteLock.writeLock().lock();
        System.out.println("開始寫入"+ key);
        map.put(key,val);
        System.out.println("寫入完畢"+ key);
        readWriteLock.writeLock().unlock();
    }

    /**
     * 讀操作
     */
    public void read(Integer key){
        readWriteLock.readLock().lock();
        System.out.println("開始讀取"+ key);
        map.get(key);
        System.out.println("讀取完畢"+ key);
        readWriteLock.readLock().unlock();
    }
}

執行結果:

開始寫入0
寫入完畢0
開始寫入7
寫入完畢7
開始寫入1
寫入完畢1
開始寫入2
寫入完畢2
開始寫入4
寫入完畢4
開始寫入3
寫入完畢3
開始寫入9
寫入完畢9
開始寫入6
寫入完畢6
開始寫入5
寫入完畢5
開始寫入8
寫入完畢8
開始讀取8
開始讀取5
開始讀取0
讀取完畢0
讀取完畢8
開始讀取1
開始讀取4
開始讀取2
讀取完畢2
開始讀取3
讀取完畢5
讀取完畢3
開始讀取9
讀取完畢9
開始讀取6
讀取完畢4
讀取完畢1
開始讀取7
讀取完畢6
讀取完畢7

很明顯,寫入的時候是是能一個線程單獨執行,讀取的時候多線程同時讀取。

寫⼊鎖也叫獨佔鎖,只能被⼀個線程佔⽤,讀取鎖也叫共享鎖,多個線程可以同時佔⽤。

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