寫這篇博客的目的是增加自己記憶,方便平常使用
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運算時,可以保證線程安全