信号量Semaphore一篇文章叫你明白
已经习惯了阿里面试官的冷笑:用过Semaphore吧,不妨说说?
本质就是 信号量模型,模型图如下:
其中的 计数器 和 等待队列 对外部是透明的,仅能通过提供的三大方法访问它们。
详细说说哪三大方法?
init()
用于设置计数器的初始值。
down()
计数器-1。若此时计数器<0,则当前线程被 阻塞。
up()
计数器+1。若此时计数器≤0,则唤醒 等待队列 中的一个线程,并将其从【等待队列】移除。有同学可能会认为这里的判断条件应该≥0,估计你是理解成生产者-消费者模式中的生产者了。可以反过来想,>0 意味着没有阻塞的线程,所以只有 ≤0 时才需要唤醒一个等待的线程。
down()、up()应配对使用,并按序使用:
先调用down(),获取锁
执行处理完后,调用up(),释放锁
若信号量init值为1,并发场景下应该不会出现>0情况,除非故意调先用up(),但这也失去了信号量的意义。
注意,这些方法都是原子性的,由信号量模型的实现方保证。JDK里的信号量模型就是由Semaphore实现,Semaphore保证了这三个方法都是原子操作。
talk is cheap,show me code?
信号量模型中的down()、up()最早被称为P操作和V操作,信号量模型也称PV原语。还有的人会用semWait()和semSignal()表达它们,叫法不同,语义都相同。JUC的acquire()、release()分别对应down()和up()。
就像信号灯,必须先检查是否为绿灯才能通过。比如累加器,count+=1操作是个临界区,只允许一个线程执行,也就是说要保证互斥。
假设线程t1、t2同时访问add(),当同时调用acquire时,由于acquire是个原子操作,仅会有一个线程(假设t1)把信号量里的计数器减为0,t2则是将计数器减为-1:
对t1,信号量里面的计数器的值是0,≥0,所以t1不会被阻塞,而是继续执行
对t2,信号量里面的计数器的值是-1,<0,所以t2被阻塞
所以此时只有t1会进入临界区执行count+=1。
当t1执行release(),信号量里计数器的值是-1,加1之后的值是0,≤0,根据up(),此时等待队列中的t2会被唤醒。于是t2在t1执行完临界区代码后,才获得进入临界区执行的机会,这就保证了互斥。
既然有JDK提供了Lock,为啥还要提供一个Semaphore ?
实现互斥锁,仅是 Semaphore的部分功能,Semaphore还可以允许多个线程访问一个临界区。
最常见的就是各种池化资源,比如数据库连接池,同一时刻,允许多个线程同时使用连接池。每个连接在被释放前,不允许其他线程使用。
对象池要求一次性创建出N个对象,之后所有的线程重复利用这N个对象,当然对象在被释放前,也是不允许其他线程使用的。所以核心就是限流器,这里的限流指不允许多于N个线程同时进入临界区。
如何快速实现一个这样的限流器呢?
那就是信号量。把计数器的值设置成对象池里对象的个数N即可:
注意这里使用的是 Vector,进入临界区的N个线程不安全。add/remove都是不安全的。比如 ArrayList remove() :
好的,请回家等通知吧!

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
参与 Apache 顶级开源项目的 N 种方式,Apache Dubbo Samples SIG 成立!
只有贡献代码才算是参与开源项目社区贡献吗? 一说到参与开源项目贡献,一般大家的反应都是代码级别的贡献,总觉得我的代码被社区合并了,我才算一个贡献者,这是一个常见的错误认知。其实,在一个开源社区中有非常多的角色是 non-code contributor,一个开源社区中的很多关键职责被大家给忽略了。 组织活动也可以是贡献社区: 你可以像远在巴西库亚巴的@fzamperin学习,为你喜欢的开源项目组织 workshop 或线下 meetup 你还可以帮助社区成员找到合适的线下峰会来提交技术议题 …… 技术写作或者技术布道也是贡献社区: 为你喜欢的开源项目编写或者改进文档 建立一个如何使用这个开源项目的 samples 将文档翻译成其他语言,帮助全球开发者认识、使用该项目 在自己的公众号或者博客分享使用该项目的指南和心得 …… 设计和官网开发也是贡献社区: 重构开源项目官网来帮助开发者更好的认识、使用该开源项目 进行用户调研来更好地改善官网导航和目录 构建一个 style guide 来帮助该项目拥有一个更统一、完善的视觉设计 为该开源项目设计贴纸、T 恤等周边 …… Apache Dubb...
- 下一篇
Electron 应用打包
为舒缓 Windows 下路径名过长的问题 issues,稍微加快 require 的速度以及简单隐匿源代码,我们可以选择把应用打包成 asar 档案文件,这只需要对源代码做一些很小的改动。大部分用户可以轻松实现这个功能,因为它在 electron-packager、electron-forge 和 electron-builder 中都得到了支持,开箱即用。 生成asar包 asar 是一种将多个文件合并成一个文件的类 tar 风格的归档格式。Electron 无需解压整个文件,就可以从其中读取任意文件内容。 可以按如下步骤来将应用打包成 asar : 安装 asar: $ npm install -g asar 使用 asar pack 打包: $ asar pack your-app app.asar 使用asar包 在 Electron 中有两类 APIs,分别是 Node.js 提供的 Node API 和 Chromium 提供的 Web API。这两种 API 都支持从 asar 包中读取文件。 Node API 由于 Electron 中打了特别补丁, Node API...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- SpringBoot2更换Tomcat为Jetty,小型站点的福音
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- SpringBoot2整合Thymeleaf,官方推荐html解决方案
- Red5直播服务器,属于Java语言的直播服务器
- CentOS关闭SELinux安全模块
- Hadoop3单机部署,实现最简伪集群
- SpringBoot2整合MyBatis,连接MySql数据库做增删改查操作
- CentOS6,CentOS7官方镜像安装Oracle11G
- Docker快速安装Oracle11G,搭建oracle11g学习环境