Java多线程工具之Semaphore(信号量)

来源:互联网 发布:淘宝商城相机 编辑:程序博客网 时间:2024/05/16 08:54

Semaphore为并发包中提供用于控制某资源同时可以被几个线程访问的类

实例代码:

[java] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. // 允许2个线程同时访问  
  2.         final Semaphore semaphore = new Semaphore(2);  
  3.         ExecutorService executorService = Executors.newCachedThreadPool();  
  4.         for (int i = 0; i < 10; i++) {  
  5.             final int index = i;   
  6.             executorService.execute(new Runnable() {  
  7.                 public void run() {  
  8.                     try {  
  9.                         semaphore.acquire();  
  10.                         // 这里可能是业务代码  
  11.                         System.out.println("线程:" + Thread.currentThread().getName() + "获得许可:" + index);  
  12.                         TimeUnit.SECONDS.sleep(1);  
  13.                         semaphore.release();  
  14.                         System.out.println("允许TASK个数:" + semaphore.availablePermits());    
  15.                     } catch (InterruptedException e) {  
  16.                         e.printStackTrace();  
  17.                     }  
  18.                 }  
  19.             });  
  20.         }  
  21.         executorService.shutdown();  


构造方法1:

[java] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. public Semaphore(int permits) {  
  2.     sync = new NonfairSync(permits);  
  3. }  

permits 初始许可数,也就是最大访问线程数


构造方法2:

[java] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. public Semaphore(int permits, boolean fair) {  
  2.     sync = (fair)? new FairSync(permits) : new NonfairSync(permits);  
  3. }  
permits 初始许可数,也就是最大访问线程数

fair 当设置为false时,线程获取许可的顺序是无序的,也就是说新线程可能会比等待的老线程会先获得许可;当设置为true时,信号量保证它们调用的顺序(即先进先出;FIFO)


主要方法:

void  acquire()   从信号量获取一个许可,如果无可用许可前 将一直阻塞等待,

void acquire(int permits)  获取指定数目的许可,如果无可用许可前  也将会一直阻塞等待

boolean tryAcquire()   从信号量尝试获取一个许可,如果无可用许可,直接返回false,不会阻塞

boolean tryAcquire(int permits)   尝试获取指定数目的许可,如果无可用许可直接返回false,

boolean tryAcquire(int permits, long timeout, TimeUnit unit)   在指定的时间内尝试从信号量中获取许可,如果在指定的时间内获取成功,返回true,否则返回false

void release()  释放一个许可,别忘了在finally中使用,注意:多次调用该方法,会使信号量的许可数增加,达到动态扩展的效果,如:初始permits 为1, 调用了两次release,最大许可会改变为2

int availablePermits() 获取当前信号量可用的许可


JDK 非公平Semaphore实现:

1.使用一个参数的构造创建Semaphore对象时,会创建一个NonfairSync对象实例,并将state值设为传入的值(permits ),

[java] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. public Semaphore(int permits) {  
  2.     sync = new NonfairSync(permits);  
  3. }  
NonfairSync间接的继承了AbstractQueuedSynchronizer实现
[java] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. final static class NonfairSync extends Sync {  
  2.         private static final long serialVersionUID = -2694183684443567898L;  
  3.   
  4.         NonfairSync(int permits) {  
  5.             super(permits);  
  6.         }  
  7.   
  8.         protected int tryAcquireShared(int acquires) {  
  9.             return nonfairTryAcquireShared(acquires);  
  10.         }  
  11.     }  
[java] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. abstract static class Sync extends AbstractQueuedSynchronizer {  
  2.     private static final long serialVersionUID = 1192457210091910933L;  
  3.   
  4.     Sync(int permits) {  
  5.         setState(permits);  
  6.     }  
AbstractQueuedSynchronizer 的setState方法

[java] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. protected final void setState(int newState) {  
  2.     state = newState;  
  3. }  


2.调用tryAcquire方法时,实际是调用NonfairSync的nonfairTryAcquireShared方法,nonfairTryAcquireShared在父类Sync中实现,

Semaphore# tryAcquire方法:

[java] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1.     public boolean tryAcquire() {  
  2.         return sync.nonfairTryAcquireShared(1) >= 0;  
  3.     }  

Sync的nonfairTryAcquireShared方法

[java] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. final int nonfairTryAcquireShared(int acquires) {  
  2.     for (;;) {  
  3.         int available = getState();  
  4.         int remaining = available - acquires;  
  5.         if (remaining < 0 ||  
  6.             compareAndSetState(available, remaining))  
  7.             return remaining;  
  8.     }  
  9. }  

nonfairTryAcquireShared方法通过获取当前的state,以此state减去需要获取信号量的个数,作为剩余个数,如果结果小于0,返回此剩余的个数;如果结果大于等于0,则基于CAS将state的值设置为剩余个数,当前步骤用到了for循环,所以只有在结果小于0或设置state值成功的情况下才会退出。

如果返回的剩余许可个数大于0,tryAcquire方法则返回true;其余返回false。


AbstractQueuedSynchronizer的compareAndSetState方法,

[java] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. protected final boolean compareAndSetState(int expect, int update) {  
  2.     // See below for intrinsics setup to support this  
  3.     return unsafe.compareAndSwapInt(this, stateOffset, expect, update);  
  4. }  

3.release方法,释放一个许可 

[java] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. public void release() {  
  2.     sync.releaseShared(1);  
  3. }  
AbstractQueuedSynchronizer的releaseShared方法,

[java] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. public final boolean releaseShared(int arg) {  
  2.     if (tryReleaseShared(arg)) {  
  3.         doReleaseShared();  
  4.         return true;  
  5.     }  
  6.     return false;  
  7. }  
release方法间接的调用了Sync的tryReleaseShared方法,该方法基于Cas 将state的值设置为state+1,一直循环确保CAS操作成功,成功后返回true。

[java] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. protected final boolean tryReleaseShared(int releases) {  
  2.     for (;;) {  
  3.         int p = getState();  
  4.         if (compareAndSetState(p, p + releases))  
  5.             return true;  
  6.     }  
  7. }  

根据上面分析,可以看得出,Semaphore采用了CAS来实现,尽量避免锁的使用,提高了性能。

0 0