Semaphore信号量通常做为控制线程并发个数的工具来使用,它可以用来限制同时并发访问资源的线程个数。

一、Semaphore使用

下面我们通过一个简单的例子来看下Semaphore的具体使用,我们同时执行10个计数线程,并定义一个Semaphore变量用来控制并发值,同一时间只允许两个线程并发执行;

public static void main(String[] args) {

Semaphore semaphore = new Semaphore(2);

// 启动计数线程

for (int i = 1; i <= 10; i++) {

new SemaphoreThread(semaphore).start();

}

}

计数线程

public class SemaphoreThread extends Thread {

private Semaphore semaphore;

public SemaphoreThread(Semaphore semaphore) {

this.semaphore = semaphore;

}

public void run() {

try {

semaphore.acquire();//获取执行许可

Thread.sleep(2000);

System.out.println(this.getName() + "线程," + "开始进行计数");

// 模拟计数时长

Thread.sleep(2000);

// 一个线程完成,允许下一个线程开始计数

System.out.println(this.getName() + "线程," + "计数完毕");

semaphore.release();//归还许可

} catch (Exception e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

}

输出结果

Thread-0线程,开始进行计数

Thread-1线程,开始进行计数

Thread-1线程,计数完毕

Thread-0线程,计数完毕

Thread-2线程,开始进行计数

Thread-3线程,开始进行计数

Thread-2线程,计数完毕

Thread-3线程,计数完毕

Thread-4线程,开始进行计数

Thread-5线程,开始进行计数

Thread-5线程,计数完毕

Thread-4线程,计数完毕

Thread-6线程,开始进行计数

Thread-7线程,开始进行计数

Thread-6线程,计数完毕

Thread-7线程,计数完毕

Thread-8线程,开始进行计数

Thread-9线程,开始进行计数

Thread-8线程,计数完毕

Thread-9线程,计数完毕

通过输出结果可以看出,Semaphore根据我们设定的并发值限制了线程同时执行的个数,每次只运行两个线程进行计数。

二、Semaphore源码分析

接下来我们对Semaphore具体的内部实现进行分析与总结

1、Semaphore的构造

public Semaphore(int permits) {

sync = new NonfairSync(permits);

}

static final class NonfairSync extends Sync {

private static final long serialVersionUID = -2694183684443567898L;

NonfairSync(int permits) {

super(permits);

}

protected int tryAcquireShared(int acquires) {

return nonfairTryAcquireShared(acquires);

}

}

abstract static class Sync extends AbstractQueuedSynchronizer {

private static final long serialVersionUID = 1192457210091910933L;

/**

1、设置AbstractQueuedSynchronizer中同步状态的值state,也就是计数器的值。

2、这个值volatile变量,必须保证线程间的可见性;

**/

Sync(int permits) {

setState(permits);

}

//获取state的值

final int getPermits() {

return getState();

}

//通过CAS方式减少state值,对应Semaphore的acquire获取许可

final int nonfairTryAcquireShared(int acquires) {

for (;;) {

int available = getState();

int remaining = available - acquires;

if (remaining < 0 ||

compareAndSetState(available, remaining))

return remaining;

}

}

//通过CAS方式增加state值,对应Semaphore的release归还许可

protected final boolean tryReleaseShared(int releases) {

for (;;) {

int current = getState();

int next = current + releases;

if (next < current) // overflow

throw new Error("Maximum permit count exceeded");

if (compareAndSetState(current, next))

return true;

}

}

//减少许可

final void reducePermits(int reductions) {

for (;;) {

int current = getState();

int next = current - reductions;

if (next > current) // underflow

throw new Error("Permit count underflow");

if (compareAndSetState(current, next))

return;

}

}

//许可置0

final int drainPermits() {

for (;;) {

int current = getState();

if (current == 0 || compareAndSetState(current, 0))

return current;

}

}

}

通过代码可以看出Semaphore也是基于AbstractQueuedSynchronizer类来实现的,它会根据你传入的并发线程数量来构造一个继承自AbstractQueuedSynchronizer的Syc实现类;

2、acquire方法

Semaphore的acquire方法实现获取执行许可,acquire方法底层调用的其实是AbstractQueuedSynchronizer的acquireSharedInterruptibly方法,我们看下具体代码

public void acquire() throws InterruptedException {

sync.acquireSharedInterruptibly(1);

}

public final void acquireSharedInterruptibly(int arg)

throws InterruptedException {

if (Thread.interrupted())

throw new InterruptedException();

//tryAcquireShared由Semaphore的Sync类的nonfairTryAcquireShared方法具体实现

if (tryAcquireShared(arg) < 0)

doAcquireSharedInterruptibly(arg);

}

从上面我们已经知道nonfairTryAcquireShared方法内部其实是一个针对state值减法操作,并通过CAS操作改变同步状态State的值,直到要获取的许可线程超过设置的并发值,tryAcquireShared(arg)返回值小于0,执行doAcquireSharedInterruptibly方法开始尝试获取锁,并进入阻塞;

3、release方法

Semaphore的release方法对应释放执行许可

public void release() {

sync.releaseShared(1);

}

public final boolean releaseShared(int arg) {

//tryAcquireShared由Semaphore的Sync类的tryReleaseShared方法具体实现,执行归还许可操作;

if (tryReleaseShared(arg)) {

//释放锁状态,唤醒阻塞线程

doReleaseShared();

return true;

}

return false;

}

执行tryReleaseShared方法归还归许可,对state值做加法操作,没有问题的话返回true值,执行doReleaseShared方法释放锁,唤醒阻塞线程。

三、总结

线程并发个数控制工具Semaphore类与CountDownLatch类似,都是基于AbstractQueuedSynchronizer类实现的,通过操作同步状态state值结合共享锁的模式控制一个或多个线程的执行从而实现具体的功能。以上就是对Semaphore类使用与源码进行的分析与总结,其中如有不足与不正确的地方还望指出与海涵。

关注微信公众号,查看更多技术文章。

作者:bigfan

java多线程工具类_Java多线程同步工具类之Semaphore相关推荐

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

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

  2. java前补零工具类_java生成编码工具类,不足补0

    ~~~~~ 小小工具类!你值得拥有 简单粗暴,直接上代码 import java.text.NumberFormat; /** * @author: Abner * @description: 编码工 ...

  3. java thread类_Java多线程原理及Thread类详解

    多线程原理 代码如下: 自定义线程类: 测试类: 流程图: 程序启动运行main时候,java虚拟机启动一个进程,主线程main在main()调用时候被创建.随着调用mt的对象的start方法,另外一 ...

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

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

  5. java线程知识梳理_Java多线程——多线程相关知识的逻辑关系梳理

    1 学习多线程知识的根本目标 多线程知识的根本目标是:设计稳健的并发程序. 当然,本文无法回答这个实践性很强的问题(这与具体的业务相关,涉及到具体的策略),本文主要阐述相关知识之间的关系,希望初学者不 ...

  6. java点名代码滚动_JAVA多线程实现简单的点名系统

    效果图如下: CMain函数: package com.shubing.main; public class CMain { public static void main(String[] args ...

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

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

  8. java线程集合点_Java多线程学习笔记(三) 甚欢篇

    使人有乍交之欢,不若使其无久处之厌 <小窗幽记>很多时候,我们需要的都不是再多一个线程,我们需要的线程是许多个,我们需要让他们配合.同时我们还有一个愿望就是复用线程,就是将线程当做一个工人 ...

  9. java 锁旗标_Java多线程

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

  10. java多线程 cpu分配_java多线程总结(转载)

    Java 多线程编程总结 --------------------------------------------------------------------------------------- ...

最新文章

  1. 打印所有低于平均分的分数(数组)
  2. 将 app 打包成 deb 发布安装
  3. 斗地主(矩阵快速幂)
  4. android 动画 返回,Android“菜单图标变返回”动画
  5. 卢伟冰:越是入门机 越应该把使用体验做好
  6. 鞋城模板+html,西安锦绣鞋城整合营销策划方案
  7. 照片墙 php源码,分享一个心型照片墙源码
  8. arcgis海岸带_需科学划定海岸带基准地理边界
  9. 从solidWorks导出机器人URDF文件
  10. 程序设计基础知识点整理,超全!!!
  11. 真正厉害的人,早戒掉了情绪
  12. 学计算机编程技校排名,学编程什么学校最好?
  13. lwip连续发数据卡死_用lwip发送大量数据时,遇到的问题解答记录 | 求索阁
  14. ASP.NET Core2.1 你不得不了解的GDPR(Cookie处理)
  15. webpack4.x的学习
  16. [Python 爬虫] 使用 Scrapy 爬取新浪微博用户信息(三) —— 数据的持久化——使用MongoDB存储爬取的数据
  17. 公交卡信息是在服务器还是卡片,北京“市政交通一卡通”卡号的相关说明
  18. python 大数据培训视频
  19. 1.7 积分上限函数的图形
  20. 视频云转码源码|php云转码系统源码双码率秒切

热门文章

  1. SpringMVC+Spring Data JPA实现增删改查操作
  2. WARN No appenders could be found for logger的解决方法
  3. 关于ping与tracert网络命令详解
  4. jaxb xml 生成 java_java-如何使用JAXB定制XML导出
  5. linux 文件夹中过滤文件内容,【shell】对指定文件夹中文件进行过滤,并修改文件内容的shell脚本...
  6. 2.自编码器(去噪自编码器,DAE)
  7. 平板Android调试打开log,Android 调试日志
  8. admixture软件_使用ADMIXTURE进行群体结构分析
  9. linux scp后台运行的办法
  10. SLAM--卡尔曼滤波、粒子滤波