什么是信号量

Java中的同步工具类信号量即计数信号量(Counting Semaphore),是用来控制访问某个特定资源的操作数量,或同时执行某个指定操作的数量。可以简单理解为信号量用来限制对某个资源的某种操作的数量。

一般用于实现某种资源池,或对容器施加边界。

信号量管理着一组有限个数的虚拟许可(permit),而许可的数量就是限制特定操作数量的关键。

信号量的使用

前面已经说过,信号量一般用于实现某种资源池或对容器施加边界,这都是一个对特定操作的限制用途。那么想象一下,如何限制操作的数量,达到为一个再普通不过的容器施加边界的效果呢?答案是给容器的某种操作(可以是添加或删除元素,应该广义的理解“某种操作”这个关键字眼)增加一道执行许可,只有在获得许可的情况下才可以执行这个操作:

上图左边是普通的对容器的操作,右边是有了信号量的对容器的操作。可以看出,在增加了中间的信号量之后,对容器的操作将会受限。

Semaphore

了解了信号量的大概含义,那么进一步深入到Java类库的层面,JDK为开发者提供了java.util.concurrent包下的Semaphore类,它的含义就是上面所述的信号量,管理着一组permit。

以“为容器施加边界”这一信号量用途为例。首先我们要明确一点,使用信号量的方式来实现施加边界的方式,其针对的是操作而不是容器的容量!再一次重申,是限制了操作,而不是容器的容量!

强调限制操作,是为了要明白一点:使用信号量来施加边界,必然会对这个容器的某些操作进一步封装。比如添加方法,就会在调用add之前先行调用Semaphore对象的acquire()方法,在与这个操作相反的操作中去release()。并且,acquire()方法是阻塞式的,这就代表没有闲置许可的时候,操作将会阻塞直到有许可被释放。

下面代码用信号量来对HashSet这个最普通的容器来施加一个添加限制,进一步封装,使其成为一个有界的阻塞式的容器

public class BoundedHashSet { private final Set set; private final Semaphore sem;  public BoundedHashSet(int bound) { this.set = Collections.synchronizedSet(new HashSet<>()); this.sem = new Semaphore(bound); }  public boolean add(T o) throws InterruptedException { sem.acquire(); boolean wasAdded = false; try { wasAdded = set.add(o); return wasAdded; } finally { if (!wasAdded) sem.release(); } }  public boolean remove(Object o) { boolean wasRemoved = set.remove(o); if (wasRemoved) sem.release(); return wasRemoved; } /** 只是为了方便打印的 */ public void print() { System.out.print(Thread.currentThread().getName() + " : "); this.set.forEach(o -> System.out.print(o + " ")); } /** 用于测试的主方法 */ public static void main(String[] args) { BoundedHashSet names = new BoundedHashSet<>(5);  new Thread(() -> { for (int i = 0; i < 100; i++) { try { names.add("name" + i);  names.print(); System.out.println(); TimeUnit.SECONDS.sleep(1); } catch (Exception e) { e.printStackTrace(); } } }, "TH-ADD").start();  new Thread(() -> { for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName() + "--------执行清理,删除name" + i); names.remove("name" + i); try { TimeUnit.SECONDS.sleep(10); } catch (Exception e) { e.printStackTrace(); } } },"TH-REMOVE").start(); }}

执行结果如下:

我们用一个线程为这个“有界”容器每隔1秒钟添加一个元素,然后另一个线程每隔10秒钟移除一个元素。且初始化了这个容器的信号量为5,那么当容器中添加元素的数量达到5之后,5个许可全部被占用,添加操作将进入阻塞状态,直到remove的时候释放一个许可,才可以继续添加元素。从上述结果可以看出两点:

1、拥有5个许可的信号量成功的限制了容器的元素个数(即为容器施加了一个边界);

2、添加的操作在没有获得许可的情况下将进入阻塞状态,在执行的过程中也恰恰印证了这一点:当remove执行并release()之后,添加操作会立刻执行。

生活中的类比

其实这个类比作者认为,从严谨的角度来讲,并不是完全符合信号量的概念,但是我们可以类比的同时找出不同点,不仅有效的通过生活案例理解了信号量,还对与之不同的地方增加了深刻的印象,所以还是决定拿出来供大家参考。

上过学的同学可能都知道,学校有奖学金制度。虽然我没怎么得过奖学金,但是大概的逻辑还是比较好理解。

学校的奖学金制度是怎样的呢?

学校每年都会给全校的学生指定数量的全额奖学金名额,比如全额奖学金5名。那么如果想获得全额奖学金,就必须先获得名额才行。

从这个简单的逻辑我们可以找出关键的与信号量中的概念相匹配的内容:

奖学金 = 特定资源

获得(奖学金) = 指定操作(如remove操作)

名额 = 一组定额许可的信号量

名额已满,来年再报 = 操作阻塞,等待释放许可

有了上面的等式,信号量的神秘面纱就算彻底被我们揭开了,原来它就是一个管理一组定额许可的通行证,要想执行操作,那就必须先得到许可,否则就阻塞。

总结

信号量的概念:限制操作数量。

一个类:Semaphore ,两个方法:acquire()、release()。

用途:对容器施加边界,对容器的操作的再封装。

另外,奖学金和信号量之间的类比并不完全匹配,不过这种程度的类比已经相当清晰,至于哪些信息有所差异,留给各位看官自己去挖掘。如果有什么新的发现,真诚希望在文章下方留言。

愿所有热爱编程的开发者共同进步!

---欢迎关注“Java圣斗士”,我是你们的小可爱(✪ω✪)---

---专注IT职场经验与IT技术分享的灵魂导师。---

---期待与您的互动哦!---

将指定的计数添加到信号量中会导致其超过_并发编程用不上?Semaphore信号量了解一下...相关推荐

  1. 将指定的计数添加到信号量中会导致其超过_从烷烃中分离烯烃、从原油中分离碳氢化合物…这七大化学分离过程将影响世界...

    <自然>(Nature)2016年刊登过一篇评论,来自佐治亚理工学院(Georgia Instituteof Technology)的两名学者归纳出了七大能源密集型分离过程,指出这些分离过 ...

  2. 将指定的计数添加到该信号量中会导致其超过最大计数

    将指定的计数添加到该信号量中会导致其超过最大计数 简介:当我在本地IIS的网站上进行快递分拣操作时,会调用快递分拣的服务WebService, 点击后程序不执行,跟踪发现出现如题错误异常,如图: 解决 ...

  3. 写出记录型信号量中的wait操作代码_操作系统进程的同步与互斥及经典同步与互斥问题...

    概览 临界区临界区的引入 在系统当中,有些资源允许多个进程共享(磁盘),有些资源只允许进程单独使用(打印机,共享变量).为了让进程单独使用资源而不受其他进程干扰引入了临界区的概念. 临界区的概念 在一 ...

  4. pv原语模拟实现_并发编程信号量的使用方法和其实现原理

    什么是信号量 信号量是并发编程中常见的一种同步机制,在需要控制访问资源的线程数量时就会用到信号量,关于什么是信号量这个问题,我引用一下维基百科对信号量的解释,大家就明白了. 信号量的概念是计算机科学家 ...

  5. java实现分而治之_并发编程中一种经典的分而治之的思想!!

    写在前面 在JDK中,提供了这样一种功能:它能够将复杂的逻辑拆分成一个个简单的逻辑来并行执行,待每个并行执行的逻辑执行完成后,再将各个结果进行汇总,得出最终的结果数据.有点像Hadoop中的MapRe ...

  6. cas无法使用_并发编程中cas的这三大问题你知道吗?

    在java中cas真的无处不在,它的全名是compare and swap,即比较和交换.它不只是一种技术更是一种思想,让我们在并发编程中保证数据原子性,除了用锁之外还多了一种选择. 一.cas的思想 ...

  7. centos7中ps显示的内容_值得收藏,史上最全Linux ps命令详解

    原标题:值得收藏,史上最全Linux ps命令详解 一.程序员的疑惑 大概在十多年前,我当时还是一个产品经理.由于一些工作的原因,需要向运维工程师学习一些linux常用命令. 当使用linux ps这 ...

  8. java中数学的头文件_计算机编程 - 数字(Numbers)

    计算机编程 - 数字(Numbers) 每种编程语言都支持操作不同类型的数字,例如简单的整数和浮点数. C,Java和Python根据其性质将这些数字分为几类. 让我们回过头来查看数据类型章节,其中列 ...

  9. spring 数组中随机取几个_游戏编程中需要掌握哪些数学物理知识

    一. 相似三角形知识的应用 在摇杆控制物体运动的游戏中,摇杆的手柄(下图黄色圆饼),不能移出摇杆所在的套(下图灰色圆环),也就是说摇杆偏离中心点的最大距离为max_R.一旦触摸移动过程中移动的点超出此 ...

最新文章

  1. SSM实现大学生综合素质评测系统
  2. 【转】VS编译时自动引用Debug|Release版本的dll
  3. 04_类与对象_课程动手动脑问题以及课后实验性问题及解答集锦
  4. 云图说|数据仓库服务 GaussDB(DWS) 的“千里眼、顺风耳”—数据库智能运维
  5. 数据科学入门与实战:玩转pandas之七数据透视
  6. webservers ajax,jQuery AJax调用asp.net webservers的实现代码
  7. mysql 提示表不存在的解决方法error: 1146: Table doesn‘t exist
  8. CSDN - markdown 编辑器模板
  9. PP实施经验分享(24)——ECN应用及系统操作
  10. mysql主从配置duxi_[从0到1搭建ABP微服务] - 搭建授权服务
  11. 操作系统--Linux操作实验报告
  12. js 根据百度地图提供经纬度计算两点距离
  13. C# LINQ标准查询操作符
  14. 【调剂】南华大学2021硕士研究生招生调剂公告
  15. 会计基础复习资料(必背内容)
  16. 如何避免ssl证书过期?
  17. c#程序设计教程 唐大仕pdf_C#程序设计教程
  18. Java操作linux脚本
  19. 局域网查看工具Lansee注册码
  20. 错误处理(二)—— Exception from HRESULT: 0x800A03EC

热门文章

  1. Maven实战 | dependencies与dependencyManagement
  2. tensorflow中random_normal的使用,案例说明,一看便知
  3. LVQ,Learning Vector Quantization,学习向量量化
  4. VS2010项目的部署与安装
  5. 怎样一次性将一个word文档中所有图片保存
  6. 在ASP.NET 3.5中使用新的ListView控件1
  7. ML《集成学习(三)Boosting和Adaboosting回归树》
  8. c语言随机抽取小程序_C语言整人小程序,慎用,谨记!
  9. leetcode - 740. 删除与获得点数
  10. 非线性优化库liblbfgs初探