官方解释:

  • 一个计数信号量。在概念上,信号量维持一组许可证。如果有必要,每个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. 哈希,hash

    Hash,一般翻译做散列.杂凑,或音译为哈希.----摘自百度百科 先来看个题:给你一坨一些键值集<key,value>,\(key\)的范围是\([1,10^{10}]\),每次询问\( ...

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

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

  3. Kubernetes-12:Secret介绍及演示

    Secret介绍 Secret存在的意义 Secret解决了密码.token.密钥等敏感数据的配置问题,而不需要把这些敏感数据暴露到镜像或者Pod Spec中,可以以Volume或者环境变量的方式使用 ...

  4. Repeater每行绑定事件代码

    if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem) { Repea ...

  5. Java 后端开发常用的 10 种第三方服务

    请肆无忌惮地点赞吧,微信搜索[沉默王二]关注这个在九朝古都洛阳苟且偷生的程序员.本文 GitHub github.com/itwanger 已收录,里面还有我精心为你准备的一线大厂面试题. 严格意义上 ...

  6. Linux:apache目录结构和配置文件详解

    bin目录下的常见命令 conf目录 htdocs目录 logs目录 httpd.conf文件解析. 如果后期自己新创建了新的站点目录,就要重新增加对应的目录权限配置 extra/目录下配置文件解析 ...

  7. 分布式系统监视zabbix讲解八之自动发现/自动注册

    自动发现(LLD) 概述 自动发现(LLD)提供了一种在计算机上为不同实体自动创建监控项,触发器和图形的方法.例如,Zabbix可以在你的机器上自动开始监控文件系统或网络接口,而无需为每个文件系统或网 ...

  8. Vue render函数 函数时组件 jsx

    常规组件使用 定义组件 components/list/list.vue <template> <ul> <li v-for="(item, index) in ...

  9. 4.Scala语法02 - 函数

  10. odoo13之在odoo中添加自定义页面

    注: 本博文是阅读Ruter博客 在odoo中添加自定义页面 后所做的个人总结,以及博文搬迁,主要是便于自己的后期理解:大部分内容为搬运,当然也包括自己的一些总结和流程优化. 前言 首先展示效果:进入 ...