多線程之--- 信號量Semaphore

寫這篇博客的目的是增加自己記憶,方便平常使用

1.信號量

Semaphore 是一種基於計數的信號量,它可以是一個閥值,基於此,多個線程競爭獲取許可信號,超過閥值後,線程申請許可信號將會別阻塞

常用方法

方法 含義
acquire() 申請一個信號量獲取可用的資源,如果可用, 信號量內部的資源個數減掉1,如果沒有可用資源線程會阻塞在該方法中,不能結束該方法,不能返回,直到有可用的資源爲止
release() 釋放資源,釋放後信號量內部的資源個數會增加1,果有被阻塞的線程,釋放後會喚醒一個線程去獲取資源,acquire() 和 release()要成對使用,一般release()放在finally代碼塊中
availablePermits() 當前可用的資源個數, permits - availablePermits() = 正在使用的資源個數

結合CountDownLatch 閉鎖 使用方式如下:

@Slf4j
public class CountExample {
    //同時併發執行的總數
    private static int threadTotal = 200;
    //請求總數
    private static int clientTotal = 5000;
    //驗證實際請求的總數   會自增1
    private static int count = 0;

    public static void main(String[] args) throws Exception{
        //創建一個可緩存的線程池,如果線程長度超過處理需要,可靈活回收線程,若無可回收,則新建線程
        ExecutorService executor = Executors.newCachedThreadPool();
       final Semaphore semaphore = new Semaphore(threadTotal);
       final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
        for (int i = 0; i < clientTotal; i++) {
            executor.execute(() -> {
                try {
                    //得到一個信號量
                    semaphore.acquire();
                    add();
                    //釋放信號量
                    semaphore.release();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    log.error("exception",e);
                }
                // 減 1
                countDownLatch.countDown();
            });
        }
        //阻塞,對計數沒任何影響
        countDownLatch.await();
        //關閉線程池
        executor.shutdown();
        log.info("完成count:{}", count);
    }

    private static void add(){
        count++;
    }

}

返回結果

[main] INFO com.mmall.concurrency.mufeng.basic.CountExample - 完成count:4970

期望值是5000,實際值爲 4970,由此可見這個執行方法是線程不安全的。

下面這個是線程安全寫法:採用AtomicInteger

@Slf4j
@ThreadSafe
public class CountExample2 {

    // 請求總數
    public static int clientTotal = 5000;

    // 同時併發執行的線程數
    public static int threadTotal = 200;

    public static AtomicInteger count = new AtomicInteger(0);

    public static void main(String[] args) throws Exception {
        ExecutorService executorService = Executors.newCachedThreadPool();
        final Semaphore semaphore = new Semaphore(threadTotal);
        final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
        for (int i = 0; i < clientTotal ; i++) {
            executorService.execute(() -> {
                try {
                    semaphore.acquire();
                    add();
                    semaphore.release();
                } catch (Exception e) {
                    log.error("exception", e);
                }
                countDownLatch.countDown();
            });
        }
        countDownLatch.await();
        executorService.shutdown();
        log.info("count:{}", count.get());
    }

    private static void add() {
        count.incrementAndGet();
        // count.getAndIncrement();
    }
}

執行結果:

[main] INFO com.mmall.concurrency.example.count.CountExample2 - count:5000

因爲採用線程安全類 AtomicInteger 所以在自增1運算時,可以保證線程安全

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