阿里云-云小站(无限量代金券发放中)
【腾讯云】云服务器、云数据库、COS、CDN、短信等热卖云产品特惠抢购

信号量Semaphore一篇文章叫你明白

32次阅读
没有评论

共计 1363 个字符,预计需要花费 4 分钟才能阅读完成。

信号量 Semaphore 一篇文章叫你明白
已经习惯了阿里面试官的冷笑:用过 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?

信号量 Semaphore 一篇文章叫你明白
信号量模型中的 down()、up()最早被称为 P 操作和 V 操作,信号量模型也称 PV 原语。还有的人会用 semWait()和 semSignal()表达它们,叫法不同,语义都相同。JUC 的 acquire()、release()分别对应 down()和 up()。

如何使用信号量?

就像信号灯,必须先检查是否为绿灯才能通过。比如累加器,count+= 1 操作是个临界区,只允许一个线程执行,也就是说要保证互斥。
信号量 Semaphore 一篇文章叫你明白
假设线程 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 即可:
信号量 Semaphore 一篇文章叫你明白
注意这里使用的是 Vector,进入临界区的 N 个线程不安全。add/remove 都是不安全的。比如 ArrayList remove():
信号量 Semaphore 一篇文章叫你明白
好的,请回家等通知吧!

阿里云 2 核 2G 服务器 3M 带宽 61 元 1 年,有高配

腾讯云新客低至 82 元 / 年,老客户 99 元 / 年

代金券:在阿里云专用满减优惠券

正文完
星哥说事-微信公众号
post-qrcode
 0
星锅
版权声明:本站原创文章,由 星锅 于2024-07-25发表,共计1363字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
【腾讯云】推广者专属福利,新客户无门槛领取总价值高达2860元代金券,每种代金券限量500张,先到先得。
阿里云-最新活动爆款每日限量供应
评论(没有评论)
验证码
【腾讯云】云服务器、云数据库、COS、CDN、短信等云产品特惠热卖中