学过操作系统的朋友都知道信号量,在java.util.concurrent包中也有一个关于信号量的实现:Semaphore。

从代码实现的角度来说,信号量与锁很类似,可以看成是一个有限的共享锁,即只能被有限数量的线程使用的共享锁。

因为存在计数,因此Semaphore的构造函数有参数permits来设定计数:

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

涉及到线程排队等待的问题,Semaphore也支持fair和unfair模式:

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

说到线程排队,前面在说“锁”的时候讲过AbstractQueuedSynchronizer,它实现了类似获取锁失败,管理等待的线程的功能。因此信号量的实现同样需要借助这个类。

abstract static class Sync extends AbstractQueuedSynchronizer

// Unfair模式的实现
static final class NonfairSync extends Sync // Fair模式的实现
static final class FairSync extends Sync

Sync类使用AbstractQueuedSynchronizer的state来存储信号量的计数:

        Sync(int permits) {
setState(permits);
}

因为信号量与共享锁类似,因此在获取资源和释放资源的时候使用的都是AbstractQueuedSynchronizer的shared类型的方法。

再次回到前面的unfair和fair模式,这种所谓的公平体现在获取锁的时候:unfair是后来先得,fair是先来先得。来看两者的尝试获取资源的方法:

        // unfair模式
final int nonfairTryAcquireShared(int acquires) {
// 直接检查是不是有资源,根本不看前面有没有其他排队的
for (;;) {
int available = getState();
int remaining = available - acquires;
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
} // fair模式
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;
}
}

对于信号量来说,获取资源的过程,就是一个更新资源计数的过程。对于释放资源来说,也是一样。

        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;
}
}

关于信号量的实现,有了AbstractQueuedSynchronizer和锁的基础,是非常好理解的。

《java.util.concurrent 包源码阅读》17 信号量 Semaphore的更多相关文章

  1. 《java.util.concurrent 包源码阅读》 结束语

    <java.util.concurrent 包源码阅读>系列文章已经全部写完了.开始的几篇文章是根据自己的读书笔记整理出来的(当时只阅读了部分的源代码),后面的大部分都是一边读源代码,一边 ...

  2. 《java.util.concurrent 包源码阅读》13 线程池系列之ThreadPoolExecutor 第三部分

    这一部分来说说线程池如何进行状态控制,即线程池的开启和关闭. 先来说说线程池的开启,这部分来看ThreadPoolExecutor构造方法: public ThreadPoolExecutor(int ...

  3. 《java.util.concurrent 包源码阅读》02 关于java.util.concurrent.atomic包

    Aomic数据类型有四种类型:AomicBoolean, AomicInteger, AomicLong, 和AomicReferrence(针对Object的)以及它们的数组类型, 还有一个特殊的A ...

  4. 《java.util.concurrent 包源码阅读》04 ConcurrentMap

    Java集合框架中的Map类型的数据结构是非线程安全,在多线程环境中使用时需要手动进行线程同步.因此在java.util.concurrent包中提供了一个线程安全版本的Map类型数据结构:Concu ...

  5. 《java.util.concurrent 包源码阅读》06 ArrayBlockingQueue

    对于BlockingQueue的具体实现,主要关注的有两点:线程安全的实现和阻塞操作的实现.所以分析ArrayBlockingQueue也是基于这两点. 对于线程安全来说,所有的添加元素的方法和拿走元 ...

  6. 《java.util.concurrent 包源码阅读》09 线程池系列之介绍篇

    concurrent包中Executor接口的主要类的关系图如下: Executor接口非常单一,就是执行一个Runnable的命令. public interface Executor { void ...

  7. 《java.util.concurrent 包源码阅读》24 Fork/Join框架之Work-Stealing

    仔细看了Doug Lea的那篇文章:A Java Fork/Join Framework 中关于Work-Stealing的部分,下面列出该算法的要点(基本是原文的翻译): 1. 每个Worker线程 ...

  8. 《java.util.concurrent 包源码阅读》25 Fork/Join框架之Fork与Work-Stealing(重写23,24)

    在写前面两篇文章23和24的时候自己有很多细节搞得不是很明白,这篇文章把Fork和Work-Stealing相关的源代码重新梳理一下. 首先来看一些线程池定义的成员变量: 关于scanGuard: v ...

  9. 《java.util.concurrent 包源码阅读》05 BlockingQueue

    想必大家都很熟悉生产者-消费者队列,生产者负责添加元素到队列,如果队列已满则会进入阻塞状态直到有消费者拿走元素.相反,消费者负责从队列中拿走元素,如果队列为空则会进入阻塞状态直到有生产者添加元素到队列 ...

随机推荐

  1. wordpress 源代码 高亮显示 (Crayon Syntax Highlighter)

    作为码农,blog 里面不贴源代码像什么话,源代码没有高亮显示,那还怎么看!一番调研之后,Crayon Syntax Highlighter 可能是最流行,功能最强大的一款代码高亮插件了. 一.安装  ...

  2. 突发小事件,USB接口问题

    昨天遇到的突发事件,突然USB接口全部瘫了,键盘鼠标全部不能用,换到别人电脑上可以,吓尿了,以为本子主板挂了,但是发现插U盘竟然可以识别而且可以打开,感觉可能是静电问题,果然,彻底关机,拔掉电池,然后 ...

  3. github上传项目

    前置说明: 1.github上已经创建好的repositories,没有的可以自己创建一个 2.已经安装好的git,下载源推荐https://pan.baidu.com/s/1kU5OCOB#list ...

  4. LeetCode 18. 4Sum (四数之和)

    Given an array S of n integers, are there elements a, b, c, and d in S such that a + b + c + d = tar ...

  5. 为什么C++没有对应realloc的new操作符呢?

    http://blog.csdn.net/rabbit729/article/details/3400260 这个用memcpy明显是有问题的.如果类有资源分配的话,直接memcpy不能复制资源,会导 ...

  6. cmd 编译java WebService

    格式:wsimport -s "src目录" -p "生成类所在包名" -keep "wsdl发布地址" 示例: wsimport -s G ...

  7. iOS 之 UITextField

    UITextField 相关细节处理: 1.  设置leftView , rightView let leftView = UIView() // 设置leftView/rightView之后,勿忘设 ...

  8. 使用bower init创建bower.json文件

    使用bower init 可以快速创建bower.json文件 bower init 回答一系列问题后就可以了,其中大部分问题可以按enter跳过.

  9. Python学习之二:Python 与 C 区别

    引自http://www.lxway.com/181844.htm 从开始看Python到现在也有半个多月了,前后看了Python核心编程和Dive into Python两本书.话说半个月看两本,是 ...

  10. (转)linux下如何批量杀JAVA进程或某个进程方法

    在工作中经常需要停止JAVA进程,停止时间也比较长,那么有时候因为一些情况,需要把 linux 下JAVA所有进程 kill 掉,又不能用killall 直接杀掉某一进程名称包含的所有运行中进程(我们 ...