例子:
以一个停车场运作为例。为了简单起见,假设停车场只有三个车位,一开始三个车位都是空的。这时如果同时来了五辆车,看门人允许其中三辆不受阻碍的进入,然后放下车拦,剩下的车则必须在入口等待,此后来的车也都不得不在入口处等待。这时,有一辆车离开停车场,看门人得知后,打开车拦,放入一辆,如果又离开两辆,则又可以放入两辆,如此往复。
在这个停车场系统中,车位是公共资源,每辆车好比一个线程,看门人起的就是信号量的作用。
更进一步,信号量的特性如下:信号量是一个非负整数(车位数),所有通过它的线程(车辆)都会将该整数减一(通过它当然是为了使用资源),当该整数值为零时,所有试图通过它的线程都将处于等待状态。在信号量上我们定义两种操作: Wait(等待) 和 Release(释放)。 当一个线程调用Wait(等待)操作时,它要么通过然后将信号量减一,要么一直等下去,直到信号量大于一或超时。Release(释放)实际上是在信号量上执行加操作,对应于车辆离开停车场,该操作之所以叫做“释放”是因为加操作实际上是释放了由信号量守护的资源。
Java并发库Semaphore 可以很轻松完成信号量控制,Semaphore可以控制某个资源可被同时访问的个数,通过 acquire() 获取一个许可,如果没有就等待,而 release() 释放一个许可。
代码示例:
1 import java.util.concurrent.ExecutorService; 2 import java.util.concurrent.Executors; 3 import java.util.concurrent.Semaphore; 4 5 public class SemaphoreTest { 6 public static void main(String[] args) { 7 ExecutorService service = Executors. newCachedThreadPool(); 8 final Semaphore sp = new Semaphore(3); 9 for (int i = 0; i < 10; i++) { 10 Runnable runnable = new Runnable() { 11 public void run() { 12 try { 13 sp.acquire(); 14 } catch (InterruptedException e1) { 15 e1.printStackTrace(); 16 } 17 System. out.println("线程" + Thread.currentThread().getName() + "进入,当前已有" + (3 - sp.availablePermits()) + "个并发" ); 18 try { 19 Thread. sleep((long) (Math. random() * 10000)); 20 } catch (InterruptedException e) { 21 e.printStackTrace(); 22 } 23 System. out.println("线程" + Thread.currentThread().getName() + "即将离开"); 24 sp.release(); 25 26 // 下面代码有时候执行不准确,因为其没有和上面的代码合成原子单元 27 // availablePermits()指的是当前信号灯库中有多少个可以被使用 28 System. out.println("线程" + Thread.currentThread().getName() + "已离开,当前已有" + (3 - sp.availablePermits()) + "个并发" ); 29 } 30 }; 31 service.execute(runnable); 32 } 33 } 34 35 }