Java并发包中Semaphore的工作原理、源码分析及使用示例
简介:
在多线程程序设计中有三个同步工具需要我们掌握,分别是Semaphore(信号量),countDownLatch(倒计数门闸锁),CyclicBarrier(可重用栅栏)
欢迎探讨,如有错误敬请指正
如需转载,请注明出处 http://www.cnblogs.com/nullzx/
1. 信号量Semaphore的介绍
我们以一个停车场运作为例来说明信号量的作用。假设停车场只有三个车位,一开始三个车位都是空的。这时如果同时来了三辆车,看门人允许其中它们进入进入,然后放下车拦。以后来的车必须在入口等待,直到停车场中有车辆离开。这时,如果有一辆车离开停车场,看门人得知后,打开车拦,放入一辆,如果又离开一辆,则又可以放入一辆,如此往复。
在这个停车场系统中,车位是公共资源,每辆车好比一个线程,看门人起的就是信号量的作用。信号量是一个非负整数,表示了当前公共资源的可用数目(在上面的例子中可以用空闲的停车位类比信号量),当一个线程要使用公共资源时(在上面的例子中可以用车辆类比线程),首先要查看信号量,如果信号量的值大于1,则将其减1,然后去占有公共资源。如果信号量的值为0,则线程会将自己阻塞,直到有其它线程释放公共资源。
在信号量上我们定义两种操作: acquire(获取) 和 release(释放)。当一个线程调用acquire操作时,它要么通过成功获取信号量(信号量减1),要么一直等下去,直到有线程释放信号量,或超时。release(释放)实际上会将信号量的值加1,然后唤醒等待的线程。
信号量主要用于两个目的,一个是用于多个共享资源的互斥使用,另一个用于并发线程数的控制。
2. 信号量Semaphore的源码分析
在Java的并发包中,Semaphore类表示信号量。Semaphore内部主要通过AQS(AbstractQueuedSynchronizer)实现线程的管理。Semaphore有两个构造函数,参数permits表示许可数,它最后传递给了AQS的state值。线程在运行时首先获取许可,如果成功,许可数就减1,线程运行,当线程运行结束就释放许可,许可数就加1。如果许可数为0,则获取失败,线程位于AQS的等待队列中,它会被其它释放许可的线程唤醒。在创建Semaphore对象的时候还可以指定它的公平性。一般常用非公平的信号量,非公平信号量是指在获取许可时先尝试获取许可,而不必关心是否已有需要获取许可的线程位于等待队列中,如果获取失败,才会入列。而公平的信号量在获取许可时首先要查看等待队列中是否已有线程,如果有则入列。
构造函数源代码
//非公平的构造函数
public Semaphore(int permits) {
sync = new NonfairSync(permits);
} //通过fair参数决定公平性
public Semaphore(int permits, boolean fair) {
sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}
acquire源代码
public void acquire() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
} public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (tryAcquireShared(arg) < 0)
doAcquireSharedInterruptibly(arg);
} final int nonfairTryAcquireShared(int acquires) {
for (;;) {
int available = getState();
int remaining = available - acquires;
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}
可以看出,如果remaining <0 即获取许可后,许可数小于0,则获取失败,在doAcquireSharedInterruptibly方法中线程会将自身阻塞,然后入列。
release源代码
public void release() {
sync.releaseShared(1);
} public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
} 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;
}
}
可以看出释放许可就是将AQS中state的值加1。然后通过doReleaseShared唤醒等待队列的第一个节点。可以看出Semaphore使用的是AQS的共享模式,等待队列中的第一个节点,如果第一个节点成功获取许可,又会唤醒下一个节点,以此类推。
3. 使用示例
package javalearning; import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore; public class SemaphoreDemo {
private Semaphore smp = new Semaphore(3);
private Random rnd = new Random(); class TaskDemo implements Runnable{
private String id;
TaskDemo(String id){
this.id = id;
}
@Override
public void run(){
try {
smp.acquire();
System.out.println("Thread " + id + " is working");
Thread.sleep(rnd.nextInt(1000));
smp.release();
System.out.println("Thread " + id + " is over");
} catch (InterruptedException e) {
}
}
} public static void main(String[] args){
SemaphoreDemo semaphoreDemo = new SemaphoreDemo();
//注意我创建的线程池类型,
ExecutorService se = Executors.newCachedThreadPool();
se.submit(semaphoreDemo.new TaskDemo("a"));
se.submit(semaphoreDemo.new TaskDemo("b"));
se.submit(semaphoreDemo.new TaskDemo("c"));
se.submit(semaphoreDemo.new TaskDemo("d"));
se.submit(semaphoreDemo.new TaskDemo("e"));
se.submit(semaphoreDemo.new TaskDemo("f"));
se.shutdown();
}
}
运行结果
Thread c is working
Thread b is working
Thread a is working
Thread c is over
Thread d is working
Thread b is over
Thread e is working
Thread a is over
Thread f is working
Thread d is over
Thread e is over
Thread f is over
可以看出,最多同时有三个线程并发执行,也可以认为有三个公共资源(比如计算机的三个串口)。
4. 参考内容
[1] http://my.oschina.net/cloudcoder/blog/362974
Java并发包中Semaphore的工作原理、源码分析及使用示例的更多相关文章
- Java并发包中CyclicBarrier的工作原理、使用示例
1. CyclicBarrier的介绍与源码分析 CyclicBarrier 的字面意思是可循环(Cyclic)使用的屏障(Barrier).它要做的事情是,让一组线程到达一个屏障(也可以叫同步点)时 ...
- 【Java】NIO中Selector的select方法源码分析
该篇博客的有些内容和在之前介绍过了,在这里再次涉及到的就不详细说了,如果有不理解请看[Java]NIO中Channel的注册源码分析, [Java]NIO中Selector的创建源码分析 Select ...
- Java并发包中CountDownLatch的工作原理、使用示例
1. CountDownLatch的介绍 CountDownLatch是一个同步工具,它主要用线程执行之间的协作.CountDownLatch 的作用和 Thread.join() 方法类似,让一些线 ...
- Java 集合系列05之 LinkedList详细介绍(源码解析)和使用示例
概要 前面,我们已经学习了ArrayList,并了解了fail-fast机制.这一章我们接着学习List的实现类——LinkedList.和学习ArrayList一样,接下来呢,我们先对Linked ...
- Java 集合系列10之 HashMap详细介绍(源码解析)和使用示例
概要 这一章,我们对HashMap进行学习.我们先对HashMap有个整体认识,然后再学习它的源码,最后再通过实例来学会使用HashMap.内容包括:第1部分 HashMap介绍第2部分 HashMa ...
- Java 集合系列11之 Hashtable详细介绍(源码解析)和使用示例
概要 前一章,我们学习了HashMap.这一章,我们对Hashtable进行学习.我们先对Hashtable有个整体认识,然后再学习它的源码,最后再通过实例来学会使用Hashtable.第1部分 Ha ...
- 【转】Java 集合系列11之 Hashtable详细介绍(源码解析)和使用示例
概要 前一章,我们学习了HashMap.这一章,我们对Hashtable进行学习.我们先对Hashtable有个整体认识,然后再学习它的源码,最后再通过实例来学会使用Hashtable.第1部分 Ha ...
- RocketMQ中Broker的HA策略源码分析
Broker的HA策略分为两部分①同步元数据②同步消息数据 同步元数据 在Slave启动时,会启动一个定时任务用来从master同步元数据 if (role == BrokerRole.SLAVE) ...
- Flink中Idle停滞流机制(源码分析)
前几天在社区群上,有人问了一个问题 既然上游最小水印会决定窗口触发,那如果我上游其中一条流突然没有了数据,我的窗口还会继续触发吗? 看到这个问题,我蒙了???? 对哈,因为我是选择上游所有流中水印最小 ...
随机推荐
- [UCSD白板题] Minimum Dot Product
Problem Introduction The dot product of two sequences \(a_1,a_2,\cdots,a_n\) and \(b_1,b_2,\cdots,b_ ...
- 介绍一些chrome 好用的插件和快捷键
1.AdBlock ★★★ 最受欢迎的Google 浏览器扩充功能,拥有超过8 百万位使用者!阻挡网路上所有的广告. 2.印象笔记 -剪裁 无需多说! 3.豆藤 Bean vine ★★★★ 豆瓣有此 ...
- Android addHeaderView和setAdapter的调用顺序后报错
在4.4之前的系统,setAdapter后再设置addHeaderView会爆 ListView想要添加headerview的话,就要通过addHeaderView这个方法,然后想要为ListView ...
- 今天自己解决了两个问题(IE10 type slow ChromeDriver erro)
都是通过google解决的,其实本应该很快解决,可是因自己粗心,大写小错了,加上java基础不过关, "webdriver.chrome.driver"中的webdriver应是全 ...
- Android--网络请求
1.Android 上发送HTTP 请求的方式一般有两种,HttpURLConnection 和 HttpClient: 2.HttpURLConnection 的用法: 1)获取 HttpURLCo ...
- Android—LayoutInflater学习笔记
LayoutInflater的的主要用处:使用LayoutInflater来载入界面,类似于findViewById()在Activity中加载Widget组件一样.区别在于与LayoutInflat ...
- Linux tcpdump 详解
简介 用简单的话来定义tcpdump,就是:dump the traffic on a network,根据使用者的定义对网络上的数据包进行截获的包分析工具. tcpdump可以将网络中传送的数据包的 ...
- velocity整合servlet
目录结构点我下载工程代码俩实体类不用说了看CustomerVelocityServlet类 package com.velocity.servlet;import java.util.ArrayLis ...
- myrocks 之数据字典
data dictionary rocksdb作为mysql的一个新的存储引擎,在存储引擎层,会维护自已的元数据信息.在innodb存储引擎中,我们通过information_schema下的INNO ...
- 【原创】还原Hyper-V 到一个新的虚拟机
Context: I need to restore VM31 backup (via 'Windows Server Backup' tool) as another new Hyper-V mac ...