您的位置:首页 > 编程语言 > Java开发

Java 信号量 Semaphore 介绍

2015-05-09 08:30 260 查看

简介

信号量(Semaphore),有时被称为信号灯,是在多线程环境下使用的一种设施,它负责协调各个线程,以保证它们能够正确、合理的使用公共资源。

一个计数信号量。从概念上讲,信号量维护了一个许可集。如有必要,在许可可用前会阻塞每一个acquire(),然后再获取该许可。每个release()添加一个许可,从而可能释放一个正在阻塞的获取者。但是,不使用实际的许可对象,Semaphore只对可用许可的号码进行计数,并采取相应的行动。拿到信号量的线程可以进入代码,否则就等待。通过acquire()和release()获取和释放访问许可。

Semaphore通常用于限制可以访问某些资源(物理或逻辑的)的线程数目。

概念

Semaphore分为单值和多值两种,前者只能被一个线程获得,后者可以被若干个线程获得。

将信号量初始化为1,使得它在使用时最多只有一个可用的许可,从而可用作一个相互排斥的锁。这通常也称为二进制信号量,因为它只能有两种状态:一个可用的许可,或零个可用的许可。按此方式使用时,二进制信号量具有某种属性(与很多
Lock
实现不同),即可以由线程释放“锁”,而不是由所有者(因为信号量没有所有权的概念)。在某些专门的上下文(如死锁恢复)中这会很有用。

此类的构造方法可选地接受一个公平参数。当设置为false时,此类不对线程获取许可的顺序做任何保证。特别地,闯入是允许的,也就是说可以在已经等待的线程前为调用
acquire()
的线程分配一个许可,从逻辑上说,就是新线程将自己置于等待线程队列的头部。当公平设置为true时,信号量保证对于任何调用
获取
方法的线程而言,都按照处理它们调用这些方法的顺序(即先进先出;FIFO)来选择线程、获得许可。注意,FIFO排序必然应用到这些方法内的指定内部执行点。所以,可能某个线程先于另一个线程调用了
acquire
,但是却在该线程之后到达排序点,并且从方法返回时也类似。还要注意,非同步的
tryAcquire
方法不使用公平设置,而是使用任意可用的许可。

通常,应该将用于控制资源访问的信号量初始化为公平的,以确保所有线程都可访问资源。为其他的种类的同步控制使用信号量时,非公平排序的吞吐量优势通常要比公平考虑更为重要。

此类还提供便捷的方法来同时
acquire
释放
多个许可。小心,在未将公平设置为true时使用这些方法会增加不确定延期的风险。

内存一致性效果:线程中调用“释放”方法(比如
release()
)之前的操作happen-before另一线程中紧跟在成功的“获取”方法(比如
acquire()
)之后的操作。

公平模式:如果以公平方式执行,则线程将会按到达的顺序(FIFO)执行,如果是非公平,则可以后请求的有可能排在队列的头部。
JDK中定义如下:
Semaphore(intpermits,booleanfair)
  创建具有给定的许可数和给定的公平设置的Semaphore。

主要的方法:

构造方法摘要
Semaphore(intpermits)

创建具有给定的许可数和非公平的公平设置的
Semaphore
Semaphore(intpermits,booleanfair)


创建具有给定的许可数和给定的公平设置的
方法摘要
void
acquire()


从此信号量获取一个许可,在提供一个许可前一直将线程阻塞,否则线程被中断。
void
acquire(intpermits)


从此信号量获取给定数目的许可,在提供这些许可前一直将线程阻塞,或者线程已被中断。
void
acquireUninterruptibly()


从此信号量中获取许可,在有可用的许可前将其阻塞。
void
acquireUninterruptibly(intpermits)


从此信号量获取给定数目的许可,在提供这些许可前一直将线程阻塞。
int
availablePermits()


返回此信号量中当前可用的许可数。
int
drainPermits()


获取并返回立即可用的所有许可。
protectedCollection<Thread>
getQueuedThreads()


返回一个collection,包含可能等待获取的线程。
int
getQueueLength()


返回正在等待获取的线程的估计数目。
boolean
hasQueuedThreads()


查询是否有线程正在等待获取。
boolean
isFair()


如果此信号量的公平设置为true,则返回
true
protected
void
reducePermits(intreduction)


根据指定的缩减量减小可用许可的数目。
void
release()


释放一个许可,将其返回给信号量。
void
release(intpermits)


释放给定数目的许可,将其返回到信号量。
String
toString()


返回标识此信号量的字符串,以及信号量的状态。
boolean
tryAcquire()


仅在调用时此信号量存在一个可用许可,才从信号量获取许可。
boolean
tryAcquire(intpermits)


仅在调用时此信号量中有给定数目的许可时,才从此信号量中获取这些许可。
boolean
tryAcquire(intpermits,
longtimeout,TimeUnitunit)


如果在给定的等待时间内此信号量有可用的所有许可,并且当前线程未被中断,则从此信号量获取给定数目的许可。
boolean
tryAcquire(longtimeout,
TimeUnitunit)


如果在给定的等待时间内,此信号量有可用的许可并且当前线程未被中断,则从此信号量获取一个许可。
Semaphore实现的功能就类似厕所有5个坑,假如有10个人要上厕所,那么同时只能有多少个人去上厕所呢?同时只能有5个人能够占用,当5个人中的任何一个人让开后,其中等待的另外5个人中又有一个人可以占用了。另外等待的5个人中可以是随机获得优先机会,也可以是按照先来后到的顺序获得机会,这取决于构造Semaphore对象时传入的参数选项。单个信号量的Semaphore对象可以实现互斥锁的功能,并且可以是由一个线程获得了“锁”,再由另一个线程释放“锁”,这可应用于死锁恢复的一些场合。

Semaphore维护了当前访问的个数,提供同步机制,控制同时访问的个数。在数据结构中链表可以保存“无限”的节点,用Semaphore可以实现有限大小的链表。另外重入锁ReentrantLock也可以实现该功能,但实现上要复杂些。

下面的Demo中申明了一个只有5个许可的Semaphore,而有20个线程要访问这个资源,通过acquire()和release()获取和释放访问许可。

packagecom.dxz.semaphore;

importjava.util.concurrent.ExecutorService;
importjava.util.concurrent.Executors;
importjava.util.concurrent.Semaphore;

publicclassTestSemaphore{
publicstaticvoidmain(String[]args){
//线程池
ExecutorServiceexec=Executors.newCachedThreadPool();
//只能5个线程同时访问
finalSemaphoresemp=newSemaphore(5);
//模拟20个客户端访问
for(intindex=0;index<20;index++){
finalintNO=index;
Runnablerun=newRunnable(){
publicvoidrun(){
try{
//获取许可
semp.acquire();
System.out.println("Accessing:"+NO);
Thread.sleep((long)(Math.random()*1000));
//访问完后,释放
semp.release();
System.out.println("semp.availablePermits()=="+semp.availablePermits());
}catch(InterruptedExceptione){
e.printStackTrace();
}
}
};
exec.execute(run);
}
//退出线程池
exec.shutdown();
}
}


执行结果如下:

Accessing:0
Accessing:2
Accessing:4
Accessing:6
Accessing:8
semp.availablePermits()==0
Accessing:10
Accessing:12
semp.availablePermits()==0
semp.availablePermits()==1
Accessing:14
semp.availablePermits()==1
Accessing:16
semp.availablePermits()==1
Accessing:18
semp.availablePermits()==1
Accessing:1
semp.availablePermits()==1
Accessing:3
semp.availablePermits()==1
Accessing:5
semp.availablePermits()==1
Accessing:7
semp.availablePermits()==1
Accessing:9
semp.availablePermits()==1
Accessing:11
Accessing:13
semp.availablePermits()==0
semp.availablePermits()==1
Accessing:15
semp.availablePermits()==1
Accessing:17
semp.availablePermits()==1
Accessing:19
semp.availablePermits()==1
semp.availablePermits()==2
semp.availablePermits()==3
semp.availablePermits()==4
semp.availablePermits()==5
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: