【并发编程】 --- Semaphore原理简介 + 使用方法

Alysa ·
更新时间:2024-09-21
· 592 次阅读

文章目录1 原理简介2 基本使用方法2.1 demo1 --- 每次获取一个许可,将线程并发数控制为N个2.2 demo2 --- 每次获取多个许可(或者说所有可获取的许可),使线程并发数变为13 其他玩法3.1 demo3 --- 尝试获取许可,如果获取不到,直接舍弃 ★★★3.2 demo4 --- 尝试一段时间内获取许可,如果获取不到,直接舍弃 ★★★

源码地址:https://github.com/nieandsun/concurrent-study.git
该工具类,在并发编程中主要用来进行并发控制,或者说限流

1 原理简介

Semaphore是信号的意思,但是我觉得将其理解为许可或者说令牌好像更好理解一些,其原理可以用下图进行表示:
在这里插入图片描述
即n个线程都想抢占运行某段代码,但是在它们运行之前必须得先去获取指定数量的许可,比如说:

(1)总共的许可就3个,哪个线程想要运行这段代码,必须获取1个许可 假如某个时间线程1、线程2、线程3获取到了许可,则这三个线程就可以运行这段代码了。其他没获取到的,必须等这三个线程中有一个释放了许可,其他线程才能拿到许可,并执行这段代码 —> 即并发数为3。

再比如说:

(2)总共的许可还是就3个,哪个线程想要运行这段代码,必须获取3个许可 那么假如某个时间线程1获取到了3个许可(注意:如果是指定必须获取3个许可,那么不会有线程一次只拿到1个或2个的情况,即要么拿到3个,要么1个没拿到),那其他线程就没法获取许可了,这个时候同一时刻,就只能有一个线程运行该方法了 —》即不存在并发的情况了。

自认为通过上面两个栗子大家肯定知道Semaphore的使用原理了。

2 基本使用方法 2.1 demo1 — 每次获取一个许可,将线程并发数控制为N个 code package com.nrsc.ch2.juctools; import lombok.extern.slf4j.Slf4j; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Semaphore; @Slf4j public class SemaphoreDemo1 { private final static int threadCount = 12; public static void main(String[] args) { ExecutorService exec = Executors.newCachedThreadPool(); final Semaphore semaphore = new Semaphore(3); for (int i = 0; i { try { semaphore.acquire(); //获取一个许可 //总共3个许可,而每次只需拿到一个许可就可以运行下面的方法 //也就是说同一时刻可以允许三个线程拿到许可,即并发数为3 test(threadNum); semaphore.release(); //释放一个许可 } catch (Exception e) { log.error("exception", e); } }); } exec.shutdown(); } private static void test(int i) throws InterruptedException { Thread.sleep(2000); log.info("threadNum:{}", i); } } 测试结果

在这里插入图片描述

2.2 demo2 — 每次获取多个许可(或者说所有可获取的许可),使线程并发数变为1 code package com.nrsc.ch2.juctools; import lombok.extern.slf4j.Slf4j; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Semaphore; @Slf4j public class SemaphoreDemo2 { private final static int threadCount = 4; public static void main(String[] args) { ExecutorService exec = Executors.newCachedThreadPool(); final Semaphore semaphore = new Semaphore(3); for (int i = 0; i { try { semaphore.acquire(3); //获取3个许可 //总共3个许可,每次都全获取了 //也就是说同一时刻只允许一个线程拿到许可,即并发数为1 test(threadNum); semaphore.release(3); //释放3个许可 } catch (Exception e) { log.error("exception", e); } }); } exec.shutdown(); } private static void test(int i) throws InterruptedException { Thread.sleep(2000); log.info("threadNum:{}", i); } } 测试结果:

在这里插入图片描述

3 其他玩法

Semaphore提供的方法还挺多的,如下图所示,但大都大同小异。相信大家看看源码中的注释肯定就知道是干什么的了。 我这里再做两个小demo演示一下tryAcquire的用法,其他用法大家可以自行探索。
在这里插入图片描述
如上图所示,tryAcquire方法有四种使用姿势:

tryAcquire() — 尝试获取一个许可 tryAcquire(long timeout, TimeUnit unit) — 尝试在某个时间段内获取一个许可 tryAcquire(int permits) — 尝试获取多个许可 tryAcquire(int permits, long timeout, TimeUnit unit) — 尝试在某个时间段内获取多个许可

看到这里我觉得大家应该都直接会用了。。。☺☺☺

需要注意的一点是: 这些方法的返回值都是boolean ,这也就提示我们,当线程竞争特别激烈的时候,我们完全可以让某些线程尝试获取一下执行权,如果实在获取不到,为了更好地保护我们的系统,我们就直接将其舍弃 — 要非常注意一下这里的舍弃

下面举两个栗子,简单演示一下其用法。

3.1 demo3 — 尝试获取许可,如果获取不到,直接舍弃 ★★★ package com.nrsc.ch2.juctools; import lombok.extern.slf4j.Slf4j; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Semaphore; @Slf4j public class SemaphoreDemo3 { private final static int threadCount = 520; public static void main(String[] args) { ExecutorService exec = Executors.newCachedThreadPool(); final Semaphore semaphore = new Semaphore(3); for (int i = 0; i { if (semaphore.tryAcquire()) { //尝试获取1个许可 //总共3个许可,而每次只需拿到一个许可就可以运行下面的方法 //也就是说同一时刻可以允许三个线程拿到许可,即并发数为3 test(threadNum); semaphore.release(); //释放3个许可 } }); } exec.shutdown(); } private static void test(int i) { try { Thread.sleep(1); //这里即使注释掉也是一样的效果 } catch (InterruptedException e) { e.printStackTrace(); } log.info("threadNum:{}", i); } }

在不看运行结果时,你会不会认为能运行test方法的线程数在(3,520)的开区间内,---- 即一开始会有3个线程抢到许可,然后1毫秒之后他们释放掉许可,其他还没开启的线程正好又可以抢到许可???

but,事实并非如此,运行结果如下:
在这里插入图片描述
由这个结果我们可以看到,事实是只要有一个线程没抢到运行的许可,其他线程就也别想再运行了。。。

是不是很神奇!!! —> 有兴趣的看源码研究到底为什么吧,我这里就不深入了。

3.2 demo4 — 尝试一段时间内获取许可,如果获取不到,直接舍弃 ★★★ code package com.nrsc.ch2.juctools; import lombok.extern.slf4j.Slf4j; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; @Slf4j public class SemaphoreDemo4 { private final static int threadCount = 520; public static void main(String[] args) { ExecutorService exec = Executors.newCachedThreadPool(); final Semaphore semaphore = new Semaphore(3); for (int i = 0; i { try { //5秒内尝试获取1个许可 if (semaphore.tryAcquire(5, TimeUnit.SECONDS)) { test(threadNum); semaphore.release(); //释放3个许可 } } catch (InterruptedException e) { e.printStackTrace(); } }); } exec.shutdown(); } private static void test(int i) { try { Thread.sleep(1000); //这里即使注释掉也是一样的效果 } catch (InterruptedException e) { e.printStackTrace(); } log.info("threadNum:{}", i); } } 运行结果如下:

由此可知,加上时间也是只要有一个线程尝试获取不到许可了,其他线程就也别想再运行了。。。

在这里插入图片描述

end


作者:nrsc



并发编程 semaphore 方法 并发

需要 登录 后方可回复, 如果你还没有账号请 注册新账号
相关文章