已经习惯了阿里面试官的冷笑:用过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() :

好的,请回家等通知吧!

信号量Semaphore一篇文章叫你明白相关推荐

  1. 一篇文章带初学者明白:什么是编译器,什么是集成开发环境(IDE)?

    各位,关于编译器和集成开发环境这两个名称,我们平时一直在说,但这二位究竟有什么区别和联系呢,今天就跟大家简单聊一聊. 我们平时所说的程序,是指双击后就可以直接运行的程序,这样的程序被称为可执行程序(E ...

  2. 关于无状态服务(stateless service) 有状态服务(stateful service),指一篇文章就搞明白

    无状态服务(stateless service) 一.定义 无状态服务(stateless service)对单次请求的处理,不依赖其他请求,也就是说,处理一次请求所需的全部信息,要么都包含在这个请求 ...

  3. 一篇文章让你明白什么是MyBatis-Plus

    目录 一.MyBatis-Plus简介 二.特性 三.支持数据库 四.框架结构 五.MyBatis-Plus的核心功能 六. 插件扩展 相关内容 Mybatis的工作流程(进大厂必备) Mybatis ...

  4. 一篇文章看明白 Android PackageManagerService 工作流程

    Android - PackageMangerService 分析 相关系列 一篇文章看明白 Android 系统启动时都干了什么 一篇文章了解相见恨晚的 Android Binder 进程间通讯机制 ...

  5. 一篇文章看明白 Android v1 v2 签名机制

    Android - v1 & v2 签名机制 相关系列 一篇文章看明白 Android 系统启动时都干了什么 一篇文章了解相见恨晚的 Android Binder 进程间通讯机制 一篇文章看明 ...

  6. 转载:一篇文章看明白 Android 系统启动时都干了什么

    最近在看<深入理解Android内核设计思想>,老实说,作为教材,这本书实在写的不好.本来内核的东西就比应用层繁杂,需要有个条绳子牵着,有个框子框着才不好跟丢.这个书老是在章节开头抛出一些 ...

  7. 一篇文章看明白 Android 图形系统 Surface 与 SurfaceFlinger 之间的关系

    Android - SurfaceFlinger 图形系统 相关系列 一篇文章看明白 Android 系统启动时都干了什么 一篇文章了解相见恨晚的 Android Binder 进程间通讯机制 一篇文 ...

  8. 一篇文章看明白 Android Service 启动过程

    Android - Service 启动过程 相关系列 一篇文章看明白 Android 系统启动时都干了什么 一篇文章了解相见恨晚的 Android Binder 进程间通讯机制 一篇文章看明白 An ...

  9. 用计算机怎么做蒙特卡洛模拟,一篇文章讲明白蒙特卡洛模拟

    先来个测试题: 用仿真的方法计算圆周率π,请写出计算步骤和简单的公式: (思考十秒钟,想不出来可以继续往下看,后面有答案) 001 什么是蒙特卡洛模拟 蒙特卡洛模拟,又称为统计实验方法 以概率论和统计 ...

最新文章

  1. 用Windows Live Writer在51CTO写博客(2018年2月24日更新)
  2. 《剑指offer》-- 复杂链表的复制、字符串的排列、数组中出现次数超过一半的数字、连续子数组的最大和
  3. 小样本点云深度学习库_小样本学习综述报告
  4. C 与 C 的真正区别在哪里?
  5. android聚焦时如何给控件加边框,edittext设置获得焦点时的边框颜色
  6. mysql多客户端数据不同步_一种多终端设备上的数据同步方法
  7. [Unity] AnimationEvent 因 Animator Controller 中 Transition 设置问题而不触发的快速解决办法:重新拖入 Animator 创建新 State
  8. java 设置月份_java – 为什么Calendar.JUNE将月份设置为7月?
  9. Strassen算法
  10. [2019南京网络赛D题]Robots
  11. LINUX报错:apt transaction returned result exit-failed
  12. c语言头文件的使用和写法,C语言头文件的使用与写法
  13. 年薪 200w 的华为员工了解一下
  14. 卡方检验python程序_卡方检验及其python实现
  15. 软件工程阶段性总结(四)——测试和维护
  16. 【VulnHub靶机渗透】一:BullDog2
  17. 如何压缩mp4视频大小
  18. 面对同行抄袭或者抹黑,应该怎么办?
  19. 计算机的正确配置文件,Windows10电脑系统如何正确配置显示器颜色配置文件
  20. 不看公式自己复原魔方

热门文章

  1. 【每日一算法】什么是二分图?
  2. 华为,你终于活成了他们害怕的样子
  3. ubuntu16 18 用着速度不错的apt源
  4. 网络应用 axIos的基本使用
  5. VUE的本地应用-V- if
  6. 【OpenCV 4开发详解】深度神经网络应用实例
  7. Glide源码解析-加载流程
  8. 查找谁在使用文件系统
  9. iOS 10道细节面试题
  10. 第三次Scream冲刺