写点什么

Semaphore 实战

用户头像
叫练
关注
发布于: 2021 年 03 月 04 日
Semaphore实战

简介


Semaphore 信号量计数器。和 CountDownLatch,CyclicBarrier 类似,是多线程协作的工具类,相对于 join,wait,notify 方法使用起来简单高效。下面我们主要看看它的用法吧!


实战


  • 限流。限制线程的并发数。

比如在一个系统中同时只能保证 5 个用户同时在线。

import java.util.concurrent.Semaphore;
/** * @author :jiaolian * @date :Created in 2021-03-04 11:13 * @description:Semaphore限流 * @modified By: * 公众号:叫练 */public class LimitCurrnet { public static void main(String[] args) throws InterruptedException { //定义20个线程,每次最多只能执行5个线程; Semaphore semaphore = new Semaphore(5); for (int i=0; i<20; i++) { new Thread(()->{ try { //获取凭证 semaphore.acquire(); System.out.println(Thread.currentThread().getName()+"登录成功"); Thread.sleep(2000); //释放凭证 semaphore.release(); System.out.println(Thread.currentThread().getName()+"用户退出"); } catch (InterruptedException e) { e.printStackTrace(); } }).start(); } }}
复制代码

如上代码所示:我们定义了 20 个用户同时访问系统,Semaphore 参数是 5,表示同时只能有 5 个用户可以获取凭证,其他用户必须等待直到有在线用户退出。调用 semaphore.acquire()表示获取凭证,此时凭证数会减一,调用 semaphore.release()表示释放凭证,凭证数会加一,如果系统中有等待的用户,操作此方法会通知等待的一个用户获取凭证成功,执行登录操作。最后打印部分结果如下:证明系统最多能保持 5 个用户同时在线。


image.png


注意:上面举出的这个案例,出个思考题:线程池是否可以实现呢?


  • 模拟 CyclicBarrier,CountDownLatch 重用!

Semaphore 可以轻松实现 CountDownLatch 计数器,CyclicBarrier 回环屏障,还记得 CountDownLatch 用法么?它是个计数器,可以帮我们统计线程执行时间,常用来测试多线程高并发执行接口效率,我们下面用 Semaphore 模拟多线程主线程等待子线程执行完毕再返回。

import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.Semaphore;/** * @author :jiaolian * @date :Created in 2021-03-01 21:04 * @description:信号量测试 * @modified By: * 公众号:叫练 */public class SemaphoreTest {
//定义线程数量; private static final int THREAD_COUNT = 2; //初始化信号量为0,默认是非公平锁 private static Semaphore semaphore = new Semaphore(0,false); private static ExecutorService executorService = Executors.newFixedThreadPool(THREAD_COUNT);
public static void main(String[] args) throws InterruptedException { for (int i=0; i<THREAD_COUNT; i++) { executorService.submit(()->{ try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"执行"); semaphore.release(); }); } //获取2个信号量 semaphore.acquire(2); System.out.println("主线程执行完毕"); executorService.shutdown(); }}
复制代码

如上代码所示:我们定义了 Semaphore 初始化信号量为 0,默认是非公平锁,在主线程中用线程池提交 2 个线程,主线程调用 semaphore.acquire(2)表示需要获取两个信号量,但此时初始化信号量为 0,此时 AQS 中的 state 会是 0-2=-2,state 值小于 0,所以主线程执行这句话会阻塞将其加入 AQS 同步队列,线程池两个线程等待 2 秒后会调用 semaphore.release()释放 2 个信号量,此时 AQS 中的 state 会自增到 0,会通知主线程退出等待继续往下执行。执行结果如下图所示。


image.png


有没有发现 Semaphore 用法可以模拟 CountDownLatch,另外 Semaphore 通过调用 acquire,release 方法,还可以实现 CyclicBarrier 功能!我们不举例了。


实现原理


  • 相同点:本质上都是计数器,底层是依赖 AQS 操作 state 实现。

  • 异同点:CountDownLatch 是共享锁实现,CyclicBarrier 是独占锁实现,CountDownLatch 通过调用 countDown 递减计数器只能使用一次,而 CyclicBarrier 通过调用 await 递减计数器可以达到“回环”重复的效果。Semaphore 也是共享锁实现,通过调用 release 计数器是递增的,通过设置信号量可以实现 CyclicBarrier,CountDownLatch 功能。


总结


今天我们介绍了 Semaphore,整理出来希望能对你有帮助,写的比不全,同时还有许多需要修正的地方,希望亲们加以指正和点评,喜欢的请点赞加关注哦。点关注,不迷路,我是叫练公众号,微信号【jiaolian123abc】边叫边练。


发布于: 2021 年 03 月 04 日阅读数: 10
用户头像

叫练

关注

我是叫练,边叫边练 2020.06.11 加入

Java高级工程师,熟悉多线程,JVM

评论

发布
暂无评论
Semaphore实战