導(dǎo)讀:這篇文章介紹的是java并發(fā)組件aqs之semaphore(信號(hào)量)
semaphore概念:
- semaphore可以控制并發(fā)訪問的線程個(gè)數(shù)坛梁,可以很容易的控制某個(gè)資源被同時(shí)訪問的個(gè)數(shù)舆声,semaphore維護(hù)了當(dāng)前訪問的個(gè)數(shù)弹灭,通過提供同步機(jī)制來控制同時(shí)訪問的個(gè)數(shù)。同時(shí)在數(shù)據(jù)結(jié)構(gòu)中鏈表正常是可以保存無限個(gè)節(jié)點(diǎn)诞仓,而semaphore可以實(shí)現(xiàn)有限大小的列表洒沦。
semaphore使用場(chǎng)景:
- 常用于僅能提供有限訪問的資源幕与。比如項(xiàng)目中使用數(shù)據(jù)庫挑势,數(shù)據(jù)庫的連接數(shù)據(jù)比如最大連接只有30,而我們上層應(yīng)用的并發(fā)數(shù)會(huì)遠(yuǎn)遠(yuǎn)超過于此啦鸣,如果同時(shí)對(duì)數(shù)據(jù)庫進(jìn)行操作就有可能出現(xiàn)無法連接數(shù)據(jù)庫而導(dǎo)致異常潮饱,這個(gè)時(shí)候就可以通過semaphore來做并發(fā)訪問控制。當(dāng)semaphore把并發(fā)控制到1的時(shí)候就跟單線程很相似了诫给。
下面通過實(shí)例來展示下semaphore的使用
- 1香拉、示列一
@Slf4j
public class SemaphoreExample1 {
private final static int threadCount = 20;
public static void main(String[] args) throws Exception {
ExecutorService exec = Executors.newCachedThreadPool();
final Semaphore semaphore = new Semaphore(3); // 同時(shí)允許三個(gè)并發(fā)訪問
for (int i = 0; i < threadCount; i++) {
final int threadNum = i;
exec.execute(() -> {
try {
semaphore.acquire(); // 獲取一個(gè)許可
test(threadNum);
semaphore.release(); // 釋放一個(gè)許可
} catch (Exception e) {
log.error("except ion", e);
}
});
}
exec.shutdown();
}
//代表業(yè)務(wù)執(zhí)行需要一秒
private static void test(int threadNum) throws Exception {
log.info("{}", threadNum);
Thread.sleep(1000);
}
}
返回結(jié)果:可以看出通過下面的返回結(jié)果,一秒只放行了3個(gè)中狂。這是因?yàn)槲覀冊(cè)趕emaphore初始化的時(shí)候定義了同時(shí)只允許三個(gè)并發(fā)訪問凫碌,而我們控制一個(gè)只獲取一個(gè)許可執(zhí)行。所以我們得到的結(jié)果是每秒有三個(gè)線程同時(shí)訪問
- 2胃榕、實(shí)例二:
@Slf4j
public class SemaphoreExample2 {
private final static int threadCount = 20;
/**
* 這里表示當(dāng)我們并發(fā)數(shù)是3的時(shí)候盛险,而一次性又獲取了三個(gè)許可,那么同一個(gè)時(shí)間內(nèi)相當(dāng)于是
* 只能一個(gè)test()調(diào)用勋又,原因是1次性就拿了三個(gè)許可去苦掘,這里面同一秒中沒有別的許可釋放出來了,這時(shí)候就和單線程很像了楔壤。
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
ExecutorService exec = Executors.newCachedThreadPool();
final Semaphore semaphore = new Semaphore(3);
for (int i = 0; i < threadCount; i++) {
final int threadNum = i;
exec.execute(() -> {
try {
semaphore.acquire(3); // 獲取三個(gè)許可
test(threadNum);
semaphore.release(3); // 釋放三個(gè)許可
} catch (Exception e) {
log.error("exception", e);
}
});
}
exec.shutdown();
}
private static void test(int threadNum) throws Exception {
log.info("{}", threadNum);
Thread.sleep(1000);
}
}
返回結(jié)果:下面的返回結(jié)果可以看出我們一秒內(nèi)只有一次test()方法調(diào)用鹤啡。這里表示當(dāng)我們并發(fā)數(shù)是3的時(shí)候,而一次性又獲取了三個(gè)許可蹲嚣,那么同一個(gè)時(shí)間內(nèi)相當(dāng)于是只能一個(gè)test()調(diào)用递瑰,原因是1次性就拿了三個(gè)許可去,這里面同一秒中沒有別的許可釋放出來了隙畜,這時(shí)候就和單線程很像了抖部。
- 3、實(shí)例三: semaphore.tryAcquire()嘗試獲取許可
@Slf4j
public class SemaphoreExample3 {
private final static int threadCount = 20;
public static void main(String[] args) throws Exception {
ExecutorService exec = Executors.newCachedThreadPool();
final Semaphore semaphore = new Semaphore(3);
for (int i = 0; i < threadCount; i++) {
final int threadNum = i;
exec.execute(() -> {
try {
if (semaphore.tryAcquire()) { // 嘗試獲取一個(gè)許可
test(threadNum);
semaphore.release(); // 釋放一個(gè)許可
}
} catch (Exception e) {
log.error("exception", e);
}
});
}
exec.shutdown();
}
private static void test(int threadNum) throws Exception {
log.info("{}", threadNum);
Thread.sleep(1000);
}
}
返回結(jié)果:下面可以看出只有三個(gè)線程輸出了日志禾蚕,而其他的線程全部丟棄了您朽。這里解釋下為什么,因?yàn)槲覀兺瑫r(shí)往線程池里放入了20個(gè)請(qǐng)求换淆,20個(gè)請(qǐng)求在同一個(gè)時(shí)間內(nèi)都會(huì)去嘗試執(zhí)行test()方法,執(zhí)行的時(shí)候我們的semaphore就會(huì)來讓每個(gè)線程來嘗試獲取許可几颜,但是同一個(gè)時(shí)間內(nèi)我們并發(fā)數(shù)是3那相當(dāng)于是只有三個(gè)線程獲取到了許可倍试,其余的17個(gè)線程都沒拿到許可直接結(jié)束了,因此控制臺(tái)只輸出了三條日志
- 4蛋哭、實(shí)例四:
@Slf4j
public class SemaphoreExample4 {
private final static int threadCount = 20;
public static void main(String[] args) throws Exception {
ExecutorService exec = Executors.newCachedThreadPool();
final Semaphore semaphore = new Semaphore(3);
for (int i = 0; i < threadCount; i++) {
final int threadNum = i;
exec.execute(() -> {
try {
// 這里表示在嘗試獲取許可的時(shí)候嘗試等待3秒鐘
if (semaphore.tryAcquire(3, TimeUnit.SECONDS)) { // 嘗試獲取一個(gè)許可
test(threadNum);
semaphore.release(); // 釋放一個(gè)許可
}
} catch (Exception e) {
log.error("exception", e);
}
});
}
exec.shutdown();
}
private static void test(int threadNum) throws Exception {
log.info("{}", threadNum);
Thread.sleep(1000);
}
}
返回結(jié)果:下面返回結(jié)果表示三秒內(nèi)的返回日志县习,因?yàn)?strong>semaphore.tryAcquire(3, TimeUnit.SECONDS)表示等待3秒鐘,而semaphore控制同時(shí)只有三個(gè)并發(fā)訪問,test()方法需要一秒的時(shí)間所以只返回了9個(gè)值躁愿,其余的因?yàn)閠ryAcquire不再等待而丟棄了叛本。
總結(jié):以上就是aqs中的semaphore的使用介紹,更多的使用可以詳細(xì)看下semaphore類彤钟,semaphore在初始化時(shí)還可以定義是否是走公平鎖還是非公平鎖来候。semaphore默認(rèn)是非公平鎖
公平鎖的作用就是嚴(yán)格按照線程啟動(dòng)的順序來執(zhí)行的,不允許其他線程插隊(duì)執(zhí)行的逸雹;而非公平鎖是允許插隊(duì)的营搅。