官方解释:

  • 一个计数信号量。在概念上,信号量维持一组许可证。如果有必要,每个acquire()都会阻塞,直到许可证可用,然后才能使用它。每个release()添加许可证,潜在地释放阻塞获取方。但是,没有使用实际的许可证对象; Semaphore只保留可用数量的计数,并相应地执行。信号量通常用于限制线程数,而不是访问某些(物理或逻辑)资源

我记得考科目一的时候有一个大教室,这个教室只能同时允许两百人考试,当有一个考完之后,下一个才能进去进行考试。门口会有安检人员进行安检,这个Semaphore就相当于这个安检员。

也可以理解为停车场,停车场内的停车位是固定的,只有当一辆或多辆车开走之后外面等待的车才能进去停车。

用法:

1、定义三个资格
Semaphore semaphore = new Semaphore(3); ThreadPoolExecutor poolExecutor =
new ThreadPoolExecutor(10, 20,
5000, TimeUnit.MILLISECONDS,
new LinkedBlockingDeque<>(100)); for (int i = 0; i < 10; i++) {
int finalI = i;
poolExecutor.execute(new Thread() {
@Override
public void run() {
try {
//获取执行资格
semaphore.acquire(1);
System.out.println(finalI+"=========");
//模拟每个线程运行的时间
Thread.sleep(1000);
//释放执行资格
semaphore.release(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
} poolExecutor.shutdown();

  

运行结果如下:(同一时刻只能运行三个线程。有点模糊,凑合看)

解析:

一、定义:

public Semaphore(int permits) {    sync = new NonfairSync(permits);}

  

Semaphroe底层也是用Sync类,默认是非公平的,也有公平的构造方法。

public Semaphore(int permits, boolean fair) {
sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}

  

定义的资格数其实是设置锁的状态值的(AQS之前已说过,维护锁状态值和线程等待队列)

abstract static class Sync extends AbstractQueuedSynchronizer {
Sync(int permits) {
setState(permits);
}
}

  

二、为什么能限制同时执行的线程数量呢?

这就是acquire方法的用处了

public void acquire(int permits) {    sync.acquireSharedInterruptibly(permits);}

  

点进acquireSharedInterruptibly这个方法看看:

public final void acquireSharedInterruptibly(int arg)
{
1、尝试获取锁,返回值小于0就是获取锁失败
if (tryAcquireShared(arg) < 0)
2、如果获取失败,则进入队列进行等待,之前已经解析过
doAcquireSharedInterruptibly(arg);
}

  

可以看到,跟之前CountDownLatch的await方法是一样的。

tryAcquireShared方法最终执行的如下方法:

final int nonfairTryAcquireShared(int acquires) {
for (;;) {
1、获取当前锁状态,锁状态值一开始是自定义的
int available = getState();
2、当前申请后剩余的锁状态值
int remaining = available - acquires;
if (3、如小于0,则申请失败,进入等待队列中
remaining < 0 ||
4、CAS替换锁状态值
compareAndSetState(available, remaining))
return remaining;
}
}

  

上述是非公平的,公平的只加了一个判断线程等待队列前是否有其它线程。排队一个一个来。

static final class FairSync extends Sync {

        protected int tryAcquireShared(int acquires) {
for (;;) {
if (hasQueuedPredecessors())
return -1;
int available = getState();
int remaining = available - acquires;
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}
}
}

  

这个就是为什么Semaphore能控制当前并发线程的数量的原因。

三、释放锁

线程获取执行资格之后需要释放锁。这就是release方法的用处。不释放的话锁会一直被占用,其他线程就无法运行。

public void release(int permits) {
if (permits < 0) throw new IllegalArgumentException();
sync.releaseShared(permits);
}

  

点进releaseShared看看

public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}

  

跟之前的CountDownLatch是一样的,只是实现不一样。Semaphore实现如下:

protected final boolean tryReleaseShared(int releases) {
for (;;) {
1、获取锁当前状态
int current = getState();
2、释放锁,直接相加
int next = current + releases;
if (next < current) // overflow
throw new Error("Maximum permit count exceeded");
3、用CAS更新锁状态
if (compareAndSetState(current, next))
return true;
}
}

  

=======================================================

我是Liusy,一个喜欢健身的程序员。

欢迎关注微信公众号【Liusy01】,一起交流Java技术及健身,获取更多干货,最新更新【K8S】。

Semaphore最详细解析的更多相关文章

  1. java类生命周期详细解析

    (一)详解java类的生命周期 引言 最近有位细心的朋友在阅读笔者的文章时,对java类的生命周期问题有一些疑惑,笔者打开百度搜了一下相关的问题,看到网上的资料很少有把这个问题讲明白的,主要是因为目前 ...

  2. springmvc 项目完整示例06 日志–log4j 参数详细解析 log4j如何配置

    Log4j由三个重要的组件构成: 日志信息的优先级 日志信息的输出目的地 日志信息的输出格式 日志信息的优先级从高到低有ERROR.WARN. INFO.DEBUG,分别用来指定这条日志信息的重要程度 ...

  3. include_path详细解析

    include_path详细解析     原文地址:http://www.laruence.com/2010/05/04/1450.html 1.php默认的包含路径为 .;C:\php\pear 即 ...

  4. Intent的详细解析以及用法

    Intent的详细解析以及用法      Android的四大组件分别为Activity .Service.BroadcastReceiver(广播接收器).ContentProvider(内容提供者 ...

  5. C++多态的实现及原理详细解析

    C++多态的实现及原理详细解析 作者: 字体:[增加 减小] 类型:转载   C++的多态性用一句话概括就是:在基类的函数前加上virtual关键字,在派生类中重写该函数,运行时将会根据对象的实际类型 ...

  6. 对MySQL DELETE语法的详细解析

    以下的文章主要描述的是MySQL DELETE语法的详细解析,首先我们是从单表语法与多表语法的示例开始的,假如你对MySQL DELETE语法的相关内容十分感兴趣的话,你就可以浏览以下的文章对其有个更 ...

  7. 转:二十一、详细解析Java中抽象类和接口的区别

    转:二十一.详细解析Java中抽象类和接口的区别 http://blog.csdn.net/liujun13579/article/details/7737670 在Java语言中, abstract ...

  8. 单表扫描,MySQL索引选择不正确 并 详细解析OPTIMIZER_TRACE格式

    单表扫描,MySQL索引选择不正确 并 详细解析OPTIMIZER_TRACE格式     一 表结构如下:  万行 CREATE TABLE t_audit_operate_log (  Fid b ...

  9. 在PHP中使用CURL,“撩”服务器只需几行——php curl详细解析和常见大坑

    在PHP中使用CURL,"撩"服务器只需几行--php curl详细解析和常见大坑 七夕啦,作为开发,妹子没得撩就"撩"下服务器吧,妹子有得撩的同学那就左拥妹子 ...

随机推荐

  1. [BUUOJ记录] [BJDCTF 2nd]文件探测

    感觉算是这次比赛里面综合性很强的一道题了,主要考察SSRF.PHP伪协议包含.挖掘逻辑漏洞和一个小tirck.委屈的是第一天晚上就做到了最后一步,想到了SESSION置空即可绕过,但是最后读Flag姿 ...

  2. 搭建一个低配版的Mock Server

    mock翻译过来是模仿的意思,Server是服务器.粗暴点直译就是模仿服务器. 写在前面 通过阅读本文,你将对Mock的使用有一定的了解,对前后端分离的概念有了更深一步的认识,对Koa的使用有一定的了 ...

  3. Mybatis源码学习第八天(总结)

    源码学习到这里就要结束了; 来总结一下吧 Mybatis的总体架构 这次源码学习我们,学习了重点的模块,在这里我想说一句,源码的学习不是要所有的都学,一行一行的去学,这是错误的,我们只需要学习核心,专 ...

  4. Java程序取MySQL的datetime字段,如何消除时间尾后讨厌的".0"

    Problem: 直接取datetime字段的内容会有".0"后缀,如2020-05-03 18:21:39.0,虽无伤大雅但总觉得有些别扭. Solution:使用转化函数Dat ...

  5. jackson读取json tree讲解

    待读取的json文本: {"data":{"count":4031,"list":[{"symbol":"SH ...

  6. 转载:使用java获取某A股当天/上一交易日的交易基本信息

    整个程序是借用了新浪的对外股票接口http://hq.sinajs.cn/list=sh603696,如果把这个地址放到浏览器地址栏里,你将看到: var hq_str_sh603696=" ...

  7. [程序员代码面试指南]递归和动态规划-最小编辑代价(DP)

    问题描述 输入 原字符串StrOrg,目标字符串StrTarget,插入.删除.替换的编辑代价ic,dc,rc.输出将原字符串编辑成目标字符串的最小代价. 解题思路 状态表示 dp[i][j]表示把s ...

  8. vue2.5开发去哪儿了流程

    初始化项目 在 src/assets 中添加样式初始化文件 reset.css ; border.css 本地引入取消延迟300毫秒的控件 cnpm i fastclick -S 在mian.js中引 ...

  9. SpringBoot框架:两个方法同时调用时父方法使内部方法的DataSource注解失效的解决办法

    一.问题如下: 使用的是SpringBoot框架:通过AOP和自定义注解完成druid连接池的动态数据源切换(三)中的两个数据库spring_boot_demo和other_data. 在UserCo ...

  10. PowerJob 从 0 到 1.9k star 的经历

    本文适合有 Java 基础知识的人群 作者:HelloGitHub-Salieri 本文就是<讲解 PowerJob>系列的最后一篇文章了,纯粹是写点经历写点心路历程啥的,和大家道个别. ...