Semaphore 信号量

一个计数信号量。从概念上讲,信号量维护了一个许可集。如有必要,在许可可用前会阻塞每一个 acquire(),然后再获取该许可。每个 release() 添加一个许可,从而可能释放一个正在阻塞的获取者。

但是,不使用实际的许可对象,Semaphore 只对可用许可的号码进行计数,并采取相应的行动。拿到信号量的线程可以进入代码,否则就等待。通过acquire()和release()获取和释放访问许可。

 1 acquire
 2 public void acquire()
 3              throws InterruptedException
 4 从此信号量获取一个许可,在提供一个许可前一直将线程阻塞,否则线程被中断。获取一个许可(如果提供了一个)并立即返回,将可用的许可数减 1 5 如果没有可用的许可,则在发生以下两种情况之一前,禁止将当前线程用于线程安排目的并使其处于休眠状态:
 6 某些其他线程调用此信号量的 release() 方法,并且当前线程是下一个要被分配许可的线程;或者
 7 其他某些线程中断当前线程。
 8 如果当前线程:
 9 被此方法将其已中断状态设置为 on ;或者
10 在等待许可时被中断。
11 则抛出 InterruptedException,并且清除当前线程的已中断状态。 
12 抛出:
13 InterruptedException - 如果当前线程被中断
1 release
2 public void release()
3 释放一个许可,将其返回给信号量。释放一个许可,将可用的许可数增加 1。如果任意线程试图获取许可,则选中一个线程并将刚刚释放的许可给予它。然后针对线程安排目的启用(或再启用)该线程。
4 不要求释放许可的线程必须通过调用 acquire() 来获取许可。通过应用程序中的编程约定来建立信号量的正确用法。

下面的例子只允许5个线程同时进入执行acquire()和release()之间的代码:

 1 public class SemaphoreTest {
 2 
 3      public static void main(String[] args) {  
 4         // 线程池 
 5         ExecutorService exec = Executors.newCachedThreadPool();  
 6         // 只能5个线程同时访问 
 7         final Semaphore semp = new Semaphore(5);  
 8         // 模拟20个客户端访问 
 9         for (int index = 0; index < 20; index++) {
10             final int NO = index;  
11             Runnable run = new Runnable() {  
12                 public void run() {  
13                     try {  
14                         // 获取许可 
15                         semp.acquire();  
16                         System.out.println("Accessing: " + NO);  
17                         Thread.sleep((long) (Math.random() * 10000));  
18                         // 访问完后,释放 ,如果屏蔽下面的语句,则在控制台只能打印5条记录,之后线程一直阻塞
19                         semp.release();  
20                     } catch (InterruptedException e) {  
21                     }  
22                 }  
23             };  
24             exec.execute(run);  
25         }  
26         // 退出线程池 
27         exec.shutdown();  
28     }  
29 }

 比较形象的解释:

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

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

再以一个停车场运作为例。为了简单起见,假设停车场只有三个车位,一开始三个车位都是空的。这时如果同时来了五辆车,看门人允许其中三辆不受阻碍的进入,然后放下车拦,剩下的车则必须在入口等待,此后来的车也都不得不在入口处等待。这时,有一辆车离开停车场,看门人得知后,打开车拦,放入一辆,如果又离开两辆,则又可以放入两辆,如此往复。

在这个停车场系统中,车位是公共资源,每辆车好比一个线程,看门人起的就是信号量的作用。

更进一步,信号量的特性如下:信号量是一个非负整数(车位数),所有通过它的线程(车辆)都会将该整数减一(使用资源),当该整数值为零时,所有试图通过它的线程都将处于等待状态。在信号量上我们定义两种操作: Wait(等待) 和 Release(释放)。 当一个线程调用Wait(等待)操作时,它要么通过然后将信号量减一,要么一直等下去,直到信号量大于一或超时。Release(释放)实际上是在信号量上执行加操作,对应于车辆离开停车场,该操作之所以叫做“释放”是因为加操作实际上是释放了由信号量守护的资源。

Semaphore(int permits, boolean fair)
//创建具有给定的许可数和给定的公平设置的Semaphore。

还可以设置该信号量是否采用公平模式,如果以公平方式执行,则线程将会按到达的顺序(FIFO)执行,如果是非公平,则可以后请求的有可能排在队列的头部。

posted @ 2017-09-18 10:51  yweihainan  阅读(580)  评论(0编辑  收藏  举报