Semaphore信号量通常做为控制线程并发个数的工具来使用,它可以用来限制同时并发访问资源的线程个数。

一、Semaphore使用

下面我们通过一个简单的例子来看下Semaphore的具体使用,我们同时执行10个计数线程,并定义一个Semaphore变量用来控制并发值,同一时间只允许两个线程并发执行;

    public static void main(String[] args) {

        Semaphore semaphore = new Semaphore(2);

        // 启动计数线程
for (int i = 1; i <= 10; i++) {
new SemaphoreThread(semaphore).start();
}
}

计数线程

public class SemaphoreThread extends Thread {

    private Semaphore semaphore;

    public SemaphoreThread(Semaphore semaphore) {
this.semaphore = semaphore;
} public void run() {
try {
semaphore.acquire();//获取执行许可
Thread.sleep(2000);
System.out.println(this.getName() + "线程," + "开始进行计数");
// 模拟计数时长
Thread.sleep(2000);
// 一个线程完成,允许下一个线程开始计数
System.out.println(this.getName() + "线程," + "计数完毕");
semaphore.release();//归还许可 } catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
} }
}

输出结果

Thread-0线程,开始进行计数
Thread-1线程,开始进行计数
Thread-1线程,计数完毕
Thread-0线程,计数完毕
Thread-2线程,开始进行计数
Thread-3线程,开始进行计数
Thread-2线程,计数完毕
Thread-3线程,计数完毕
Thread-4线程,开始进行计数
Thread-5线程,开始进行计数
Thread-5线程,计数完毕
Thread-4线程,计数完毕
Thread-6线程,开始进行计数
Thread-7线程,开始进行计数
Thread-6线程,计数完毕
Thread-7线程,计数完毕
Thread-8线程,开始进行计数
Thread-9线程,开始进行计数
Thread-8线程,计数完毕
Thread-9线程,计数完毕

通过输出结果可以看出,Semaphore根据我们设定的并发值限制了线程同时执行的个数,每次只运行两个线程进行计数。

二、Semaphore源码分析

接下来我们对Semaphore具体的内部实现进行分析与总结

1、Semaphore的构造

public Semaphore(int permits) {
sync = new NonfairSync(permits);
} static final class NonfairSync extends Sync {
private static final long serialVersionUID = -2694183684443567898L; NonfairSync(int permits) {
super(permits);
} protected int tryAcquireShared(int acquires) {
return nonfairTryAcquireShared(acquires);
}
} abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = 1192457210091910933L;
/**
1、设置AbstractQueuedSynchronizer中同步状态的值state,也就是计数器的值。
2、这个值volatile变量,必须保证线程间的可见性;
**/
Sync(int permits) {
setState(permits);
} //获取state的值
final int getPermits() {
return getState();
} //通过CAS方式减少state值,对应Semaphore的acquire获取许可
final int nonfairTryAcquireShared(int acquires) {
for (;;) {
int available = getState();
int remaining = available - acquires;

if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
} //通过CAS方式增加state值,对应Semaphore的release归还许可
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;
}
} //减少许可
final void reducePermits(int reductions) {
for (;;) {
int current = getState();
int next = current - reductions;
if (next > current) // underflow
throw new Error("Permit count underflow");
if (compareAndSetState(current, next))
return;
}
} //许可置0
final int drainPermits() {
for (;;) {
int current = getState();
if (current == 0 || compareAndSetState(current, 0))
return current;
}
}
}

通过代码可以看出Semaphore也是基于AbstractQueuedSynchronizer类来实现的,它会根据你传入的并发线程数量来构造一个继承自AbstractQueuedSynchronizer的Syc实现类;

2、acquire方法

Semaphore的acquire方法实现获取执行许可,acquire方法底层调用的其实是AbstractQueuedSynchronizer的acquireSharedInterruptibly方法,我们看下具体代码

    public void acquire() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
} public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
//tryAcquireShared由Semaphore的Sync类的nonfairTryAcquireShared方法具体实现
if (tryAcquireShared(arg) < 0)
doAcquireSharedInterruptibly(arg);
}

从上面我们已经知道nonfairTryAcquireShared方法内部其实是一个针对state值减法操作,并通过CAS操作改变同步状态State的值,直到要获取的许可线程超过设置的并发值,tryAcquireShared(arg)返回值小于0,执行doAcquireSharedInterruptibly方法开始尝试获取锁,并进入阻塞;

3、release方法

Semaphore的release方法对应释放执行许可

    public void release() {
sync.releaseShared(1);
}
public final boolean releaseShared(int arg) {
//tryAcquireShared由Semaphore的Sync类的tryReleaseShared方法具体实现,执行归还许可操作;
if (tryReleaseShared(arg)) {
//释放锁状态,唤醒阻塞线程
doReleaseShared();
return true;
}
return false;
}

执行tryReleaseShared方法归还归许可,对state值做加法操作,没有问题的话返回true值,执行doReleaseShared方法释放锁,唤醒阻塞线程。

三、总结

线程并发个数控制工具Semaphore类与CountDownLatch类似,都是基于AbstractQueuedSynchronizer类实现的,通过操作同步状态state值结合共享锁的模式控制一个或多个线程的执行从而实现具体的功能。以上就是对Semaphore类使用与源码进行的分析与总结,其中如有不足与不正确的地方还望指出与海涵。

关注微信公众号,查看更多技术文章。

Java多线程同步工具类之Semaphore的更多相关文章

  1. Java多线程并发工具类-信号量Semaphore对象讲解

    Java多线程并发工具类-Semaphore对象讲解 通过前面的学习,我们已经知道了Java多线程并发场景中使用比较多的两个工具类:做加法的CycliBarrier对象以及做减法的CountDownL ...

  2. Java多线程同步工具类之CountDownLatch

    在过去我们实现多线程同步的代码中,往往使用join().wait().notiyAll()等线程间通信的方式,随着JUC包的不断的完善,java为我们提供了丰富同步工具类,官方也鼓励我们使用工具类来实 ...

  3. Java多线程同步工具类之CyclicBarrier

    一.CyclicBarrier使用 CyclicBarrier从字面上可以直接理解为线程运行的屏障,它可以让一组线程执行到一个共同的屏障点时被阻塞,直到最后一个线程执行到指定位置,你设置的执行线程就会 ...

  4. java 利用同步工具类控制线程

    前言 参考来源:<java并发编程实战> 同步工具类:根据工具类的自身状态来协调线程的控制流.通过同步工具类,来协调线程之间的行为. 可见性:在多线程环境下,当某个属性被其他线程修改后,其 ...

  5. Java多线程并发工具类

    Semaphore-信号灯机制 当我们创建一个可扩展大小的线程池,并且需要在线程池内同时让有限数目的线程并发运行时,就需要用到Semaphore(信号灯机制),Semaphore 通常用于限制可以访问 ...

  6. Java并发——同步工具类

    CountDownLatch  同步倒数计数器 CountDownLatch是一个同步倒数计数器.CountDownLatch允许一个或多个线程等待其他线程完成操作. CountDownLatch对象 ...

  7. Java多线程——其他工具类CyclicBarrier、CountDownLatch和Exchange

    CyclicBarrier 适用于:创建一组任务,它们并行地执行任务,然后在进行下一个步骤之前等待,直至所有任务完成.它使得所有的并行任务都将在栅栏处列队,因此可以一致地向前移动. 表示大家彼此等待, ...

  8. JUC——线程同步辅助工具类(Semaphore,CountDownLatch,CyclicBarrier)

    锁的机制从整体的运行转态来讲核心就是:阻塞,解除阻塞,但是如果仅仅是这点功能,那么JUC并不能称为一个优秀的线程开发框架,然而是因为在juc里面提供了大量方便的同步工具辅助类. Semaphore信号 ...

  9. Java并发多线程 - 并发工具类JUC

    安全共享对象策略 1.线程限制 : 一个被线程限制的对象,由线程独占,并且只能被占有它的线程修改 2.共享只读 : 一个共享只读的对象,在没有额外同步的情况下,可以被多个线程并发访问, 但是任何线程都 ...

随机推荐

  1. 测试了下boost的序列化反序列化功能

    // testSerialization.cpp : Defines the entry point for the console application. // #include "st ...

  2. Windows静态库和动态库的创建和使用(VS2005)

    偶们在实际的编程开发中,经常会遇到运行时无法找到某个DLL文件或者链接时无法找到某个LIB文件.然后,我们就开始乱GOOGLE一下,然后将VS2005的设置改变一下,或许就Ok了,我们将别人开发的DL ...

  3. 【WPF】UI虚拟化之------自定义VirtualizingWrapPanel

    原文:[WPF]UI虚拟化之------自定义VirtualizingWrapPanel 前言 前几天QA报了一个关于OOM的bug,在排查的过程中发现,ListBox控件中被塞入了过多的Item,而 ...

  4. NET实现RSA AES DES 字符串 加密解密以及SHA1 MD5加密

    本文列举了    数据加密算法(Data Encryption Algorithm,DEA) 密码学中的高级加密标准(Advanced EncryptionStandard,AES)RSA公钥加密算法 ...

  5. 关于WPF的ComboBox中Items太多而导致加载过慢的问题

    原文:关于WPF的ComboBox中Items太多而导致加载过慢的问题 [WFP疑难]关于WPF的ComboBox中Items太多而导致加载过慢的问题                          ...

  6. GlusterFS集群文件系统研究(负载均衡的最常用办法) good

    http://blog.csdn.net/liuaigui/article/details/6284551 http://blog.csdn.net/liuaigui/article/details/ ...

  7. 零元学Expression Blend 4 - Chapter 27 PathlistBox被Selected时的蓝底蓝框问题

    原文:零元学Expression Blend 4 - Chapter 27 PathlistBox被Selected时的蓝底蓝框问题 最近收到网友Cloud的来信,询问我有关放进PathlistBox ...

  8. SignalR---服务端

    原文:SignalR---服务端 前段时间把SignalR的官网教程大致看了一下,算是翻译了一遍,加上了自己的个人理解, 一下上传三个文件,分别是服务端.web客户端.DOTNET客户端相关文档,供大 ...

  9. tf.nn.softmax & tf.nn.reduce_sum & tf.nn.softmax_cross_entropy_with_logits

    tf.nn.softmax softmax是神经网络的最后一层将实数空间映射到概率空间的常用方法,公式如下: \[ softmax(x)_i=\frac{exp(x_i)}{\sum_jexp(x_j ...

  10. UWP实现时钟

    UWP现在的开发确实很方便,不过资料真的好少啊... 前些天看到同学有实实现自定义的时钟,这东东挺简单的,就自己也写个,没成想,遇到个坑,费了好长时间,记下来一下. 效果图: 画个圆,三条线就好.XA ...