官方解释:

  • 一个计数信号量。在概念上,信号量维持一组许可证。如果有必要,每个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. linux的五种IO模型

    概念: 同步.异步.阻塞.非阻塞的概念 同步:所谓同步,发起一个功能调用的时候,在没有得到结果之前,该调用不返回,也就是必须一件事一件事的做,等前一件做完了,才能做下一件. 提交请求->等待服务 ...

  2. 深入了解Kafka【五】Partition和消费者的关系

    1.消费者与Partition 以下来自<kafak权威指南>第4章. 假设主题T1有四个分区. 1.1.一个消费者组 1.1.1.消费者数量小于分区数量 只有一个消费者时,消费者1将收到 ...

  3. java安全编码指南之:声明和初始化

    目录 简介 初始化顺序 循环初始化 不要使用java标准库中的类名作为自己的类名 不要在增强的for语句中修改变量值 简介 在java对象和字段的初始化过程中会遇到哪些安全性问题呢?一起来看看吧. 初 ...

  4. Angular(二) - 组件Component

    1. 组件Component示例 2. Component常用的几个选项 3. Component全部的选项 3.1 继承自@Directive装饰器的选项 3.2 @Component自己特有的选项 ...

  5. DVWA_sql injection(low)

    这里主要记录下做题时的思路以及步骤,不查看源码也不对源码进行分析.(我这个dvwa是一个在线靶场,所以我也不确定是否与本地靶场有些许出入) 1.Low 将难度调为Low级别后,来到如下界面 首先输入一 ...

  6. Oracle12C创建scott账户

    Oracle12C创建scott账户 具体步骤: 打开SqlPlus 输入用户名和密码用户名:sys口令:sys as sysdba 创建c##scott用户CREATE USER c##scott ...

  7. Zabbix如何解决“System time is out of sync (diff with Zabbix server > 60s)”告警

    Zabbix如何解决"System time is out of sync (diff with Zabbix server > 60s)"这种告警呢? 这个错误对应的中文提 ...

  8. C:将算术表达式的符号和数分开

    程序: #include <stdio.h> #include <string.h> static int pos=; static char* line; void test ...

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

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

  10. selenuim中18种定位方式

    18种定位方式=8种单数形式+8种复数形式+2种底层方案 单数可以确定唯一,复数无法确定: 单数形式定位,返回的是一个元素,复数形式,返回的是一个列表,返回的是当前页面所有符合要求的元素,没有意义 一 ...