信号量(Semaphore)是Java多线程兵法中的一种JDK内置同步器,通过它可以实现多线程对公共资源的并发访问控制。一个线程在进入公共资源时需要先获取一个许可,如果获取不到许可则要等待其它线程释放许可,每个线程在离开公共资源时都会释放许可。其实可以将Semaphore看成一个计数器,当计数器的值小于许可最大值时,所有调用acquire方法的线程都可以得到一个许可从而往下执行。而调用release方法则可以让计数器的值减一。

信号量的主要应用场景是控制最多N个线程同时地访问资源,其中计数器的最大值即是许可的最大值N。以停车场为例,假设停车场一共有8个车位,其中6个车位已被停放,然后来了两辆汽车,此时因为刚好剩下两个车位所以这两辆车都能停放。接着又来了一辆车,现在已经没有空位了所以只能等待其它车离开。此时刚好一辆红色汽车离开停车场,来开后黄车刚好可以停进去,假如又有一辆汽车进来则该车又得等待。如此往复。这个过程中停车场就是公共资源,车位数就是信号量最大许可数,车辆就好比线程。

四要素

信号量的四要素为:最大许可数、公平模式、acquire方法以及release方法。最大许可数和公平模式在构建Semaphore对象时指定,分别表示公共资源最多可以多少个线程同时访问以及获取许可时是否使用公平模式。acquire方法用于获取许可,假如因为不足许可的话则进入等待状态。release方法用于释放许可。

非公平模式的实现

Semaphore类的实现是基于AQS同步器来实现的,不管是公平模式还是非公平模式都是基于AQS的共享模式,只是在获取许可的操作逻辑有差异。Semaphore的默认模式为非公平模式,我们先看非公平模式的实现。

Semaphore类的几个主要方法如下所示,其中提供了两个构造函数,相关的两个参数为许可最大数和是否使用公平模式,其中FairSync是公平模式的同步器而NonfairSync则是非公平模式的同步器。有两个acquire方法,无参时默认是一次获取1个许可,而如果传入整型参数则表示一次获取若干个许可。对应地,也有两个release方法,无参时表示释放1个许可,而整型参数则表示一次释放若干个许可。Semaphore主要的几个方法如下

Semaphore内部的Syn子类是公平模式FairSync类和非公平模式NonfairSync类的抽象父类,许可最大数与AQS同步器的状态变量对应。因为模式是非公平模式,所以这里提供了非公平的许可获取方法nonfairTryAcquireShared。非公平模式其实就是在许可数量允许的情况下,让所有线程都进行自旋操作,而不管它们先来后到的顺序,全部线程放到一起去竞争许可。其中compareAndSetState方法提供了CAS算法从而能够保证并发修改许可值,而剩余许可数等于当前可用许可值减去当前消耗许可数,需要注意的是当剩余许可数小于0时则返回负数从而导致线程会进入等待队列中。tryReleaseShared方法则提供了释放许可的操作,不管是不是公平模式都使用该方法即可,释放许可的逻辑是相同的。通过自旋操作来将释放的许可数增加到当前剩余许可数。

非公平模式NonfairSync类的实现主要是tryAcquireShared方法,直接调用父类Sync的的nonfairTryAcquireShared方法即可。

非公平同步器

公平模式的实现

公平模式与非公平模式的主要差异就在获取许可时的机制,非公平模式直接通过自旋操作让所有线程竞争许可,从而导致了非公平。而公平模式则通过队列来实现公平机制。它们的差异就在tryAcquireShared方法,我们看公平模式的tryAcquireShared方法。实际上不同的地方就在下图中加了方框的两行代码,它会检查是否已经存在等待队列,如果已经有等待队列则返回-1,返回-1则表示让AQS同步器将当前线程进入等待队列中,队列则意味着公平。实际上,这也并非是严格的公平,在前面讲到的AQS同步器的公平性章节有深入讲过AQS的公平性,如果忘记了可以重新查阅加深理解。而且在为达到最大许可数的情况下,所有线程也并没有进入等待队列中,而是全部线程进行自旋获取许可。

公平模式的实现

例子一

我们先看一个简单的例子,首先实例化一个拥有5个许可的信号量对象,然后一共有10个线程一同尝试获取5个许可,得到许可的线程将value进行累加1,接着睡眠五秒,最后释放许可。

以上程序输出如下,其中有五个线程输出“counting number : xx”后其他线程则开始等待。大概等待5秒后获得许可的五个线程执行释放许可操作,然后其它线程才能获得许可并往下执行。

例子二

例子二与例子一很相似,不同的地方在于每次获取许可时会消耗2个许可,同样释放时也释放2个许可。这里实例化一个拥有6个许可的信号量对象,然后10个线程一同尝试获取许可。但这次最多只能同时3个线程得到许可,也就是三个线程得到许可后对value值进行累加1,然后睡眠5秒后释放许可。接着另外三个线程又获得许可往下执行,直到10个线程都执行完。

总结

本文介绍了一个JDK内置的同步器——信号量(Semaphore),通过它能够控制最多若干个线程访问公共资源。它可以看成是一个计数器,当计数器的值小于许可最大值时线程能够往下执行,反之线程则只能等待。我们深入分析了Semaphore的实现原理,它基于AQS同步器进行实现且提供了公平和非公平两种模式,并且我们对这两种模式的实现分别进行了分析。通过本文我们已经能够很深入清晰理解Semaphore的原理机制了。

java多线程的同步控制_Java多线程并发控制工具信号量Semaphore,实现原理及案例...相关推荐

  1. Java多线程并发控制工具CountDownLatch,实现原理及案例

    跟着作者的65节课彻底搞懂Java并发原理专栏,一步步彻底搞懂Java并发原理. 作者简介:笔名seaboat,擅长工程算法.人工智能算法.自然语言处理.架构.分布式.高并发.大数据和搜索引擎等方面的 ...

  2. java进程与线程_Java多线程笔记(零):进程、线程与通用概念

    前言 不积跬步,无以至千里:不积小流,无以成江海.在学习Java多线程相关的知识前,我们首先需要去了解一点操作系统的进程.线程以及相关的基础概念. 进程 通常,我们把一个程序的执行称为一个进程.反过来 ...

  3. java同步方法的特点_java多线程有哪些优点?同步实例代码展示

    在我们的日常学习当中会发现java的知识点是总是息息相关的,可以串联起来.java中多线程的有关内容可以衍生出更多知识,它的优缺点也是非常明显的.你都了解吗?一起来看看吧. 首先为大家介绍一下,多线程 ...

  4. java线程代码实现_Java 多线程代码实现讲解

    作为一个完全面向对象的语言,Java提供了类 java.lang.Thread 来方便多线程编程,这个类提供了大量的方法来方便我们控制自己的各个线程.那么如何提供给 Java 我们要线程执行的代码呢? ...

  5. java 锁旗标_Java多线程

    Java多线程 1. 多线程存在的意义 多线程最大的作用就是能够创建"灵活响应"的桌面程序,而编写多线程最大的困难就是不同线程之间共享资源的问题,要使这些资源不会同时被多个线程访问 ...

  6. java线程怎么用_Java多线程基本使用

    一.概念 1.进程 1.1进程:是一个正在进行中的程序,每一个进程执行都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制单元. 1.2线程:就是进程中一个独立的控制单元,线程在控制着进程的执行,一 ...

  7. 秒杀多线程第八篇 经典线程同步 信号量Semaphore

    阅读本篇之前推荐阅读以下姊妹篇: <秒杀多线程第四篇一个经典的多线程同步问题> <秒杀多线程第五篇经典线程同步关键段CS> <秒杀多线程第六篇经典线程同步事件Event& ...

  8. java多线程并发实例_JAVA多线程的并发控制|java多线程并发实例

    java的多线程实现主要有两种,一种是继承Thread,一种是实现Runnable接口,这个是java最基本的多线程知识.这里要补充一下,runnable接口中的run方法是不返回任何内容的,如果想返 ...

  9. java 多线程并发 问题_JAVA多线程和并发基础面试问答

    原文链接 译文连接作者:Pankaj  译者:郑旭东  校对:方腾飞 多线程和并发问题是Java技术面试中面试官比较喜欢问的问题之一.在这里,从面试的角度列出了大部分重要的问题,但是你仍然应该牢固的掌 ...

最新文章

  1. 【数学知识】三种方法求 [1,n] 中所有数欧拉函数(线性筛欧拉函数优化至 O(n) )
  2. 简单了解request与response
  3. echarts 地图 scatter点击事件_React实现高亮可点击地图
  4. UIBarButtonItem使用困惑
  5. ubuntu安装使用ffmpeg
  6. vue使用element日期选择器,选择日期少一天的问题
  7. mac homebrew
  8. 如何修改 SAP Spartacus CMS API 默认的 endpoint
  9. upload-labs_pass20-move_uploaded_file函数特性
  10. Websockets与Spring 4
  11. 早秋精品电商男装页面\海报设计PSD模板
  12. C#开发命令执行驱动程序 之 控制标志的命令行参数
  13. java 乘法 位移_Java:移位实现的乘除法
  14. 模版 ----- 一维指数型枚举 排列型枚举 组合型枚举
  15. “编程能力差,90%输在了这点上!”骨灰级工程师:其实你们都是瞎努力!
  16. Linux常用命令--uname
  17. C++11 现代C++风格的新元素--简介
  18. html5仿微博弹出,JS 仿腾讯发表微博的效果代码
  19. 计算机网络中最常用的三种设备,计算机网络基础试卷6
  20. 【备忘】LAMP兄弟连李明老师讲Linux[更新完毕-共享完毕]

热门文章

  1. mysql不同服务器查询_实战操作SQL Server连接查询不同服务器表数据
  2. 使用 grep 查找所有包含指定文本的文件
  3. 洛谷 P1086 花生采摘
  4. hdu4734 F(x)
  5. 【新闻发布系统】项目文档
  6. codeforces 796A-D
  7. windows8.1 plsql连接oracle
  8. bzoj 1040: [ZJOI2008]骑士
  9. .Net 如何限制用户登录(转)
  10. [读书笔记]一些编码中需要注意的东西