Semaphore - Semaphore Mechanism

Semaphore (semaphore mechanism) is used when we create a thread pool of scalable size and need to allow a limited number of threads to run concurrently in the thread pool. Semaphore is usually used to restrict access to certain resources (physical or logical). ), it is a counting semaphore, conceptually, the semaphore maintains a set of permits, and if necessary, blocks each acquire() until the permit is available, then acquires that permit, and each release() adds a permission, thereby potentially releasing a blocking acquirer.        

        When threads are created and run within the thread pool, each thread must acquire a permission from the semaphore, which guarantees that the item can be used. After the thread ends, the thread returns to the pool and returns the permission to the semaphore, allowing other threads to acquire the item. Note that the synchronization lock cannot be held when acquire() is called, as this would prevent the thread from returning to the pool. A semaphore encapsulates the synchronization needed to limit access to the pool, separate from the synchronization needed to maintain consistency on the pool itself. Here is an example to illustrate:

 

[java]  view plain copy  
 
  1. publicclass SemaphoreTest {   
  2.     publicstaticvoid main(String[] args) {    
  3.         ExecutorService service = Executors.newCachedThreadPool();  
  4.         final  Semaphore sp = new Semaphore(3);  
  5.         for(int i=0;i<5;i++){  
  6.             Runnable runnable = new Runnable(){  
  7.                     publicvoid run(){   
  8.                     try {  
  9.                         sp.acquire();  
  10.                     } catch (InterruptedException e1) {  
  11.                         e1.printStackTrace();  
  12.                     }  
  13.                     System.out.println("线程" + Thread.currentThread().getName() +   
  14.                             "Enter, currently available"  + ( 3 -sp.availablePermits()) +  "concurrent" );  
  15.                     try {  
  16.                         Thread.sleep((long)(Math.random()*10000));  
  17.                     } catch (InterruptedException e) {  
  18.                         e.printStackTrace ();  
  19.                     }  
  20.                     System.out.println("线程" + Thread.currentThread().getName() +   
  21.                             "about to leave" );                      
  22.                     sp.release();  
  23.                     //The following code sometimes executes inaccurately because it does not synthesize atomic units with the above code  
  24.                     System.out.println("线程" + Thread.currentThread().getName() +   
  25.                             "left, currently available"  + ( 3 -sp.availablePermits()) +  "concurrent" );  
  26.                 }  
  27.             };  
  28.             service.execute(runnable);            
  29.         }  
  30.     }  
  31. }  
 

 

       该例子定义了一个newCachedThreadPool,在该Pool中利用for循环同时创建5个线程,现在通过Semaphore,创建一个只允许在线程池中有3个线程并发运行,sp.acquire()表示某个线程获得了一个信号灯,开始运行,在运行结束时,通过sp.release()还回这个信号灯,以便剩下的线程获得信号灯运行,sp.availablePermits()指的是当前信号灯库中有多少个可以被使用,由于例子中定义有3个信号灯,所以3-sp.availablePermits()就代表了当前有多少个线程在并发运行,上例运行结果如下:

 

[java]  view plain  copy
 
  1. 线程pool-1-thread-1进入,当前已有2个并发  
  2. 线程pool-1-thread-2进入,当前已有2个并发  
  3. 线程pool-1-thread-3进入,当前已有3个并发  
  4. 线程pool-1-thread-1即将离开  
  5. 线程pool-1-thread-1已离开,当前已有2个并发  
  6. 线程pool-1-thread-4进入,当前已有3个并发  
  7. 线程pool-1-thread-3即将离开  
  8. 线程pool-1-thread-3已离开,当前已有2个并发  
  9. 线程pool-1-thread-5进入,当前已有3个并发  
  10. 线程pool-1-thread-2即将离开  
  11. 线程pool-1-thread-2已离开,当前已有2个并发  
  12. 线程pool-1-thread-4即将离开  
  13. 线程pool-1-thread-4已离开,当前已有1个并发  
  14. 线程pool-1-thread-5即将离开  
  15. 线程pool-1-thread-5已离开,当前已有0个并发  

 

     

Semaphore作为互斥锁使用:

       当信号量初始化为 1,使得它在使用时最多只有一个可用的许可,从而可用作一个相互排斥的锁。这通常也称为二进制信号量,因为它只能有两种状态:一个可用的许可,或零个可用的许可。按此方式使用时,与传统互斥锁最大不同就是在释放的时候并不是必须要拥有锁的对象释放,也可以由其他的对象释放,因为信号量没有所有权的概念。在某些专门的上下文(如死锁恢复)中这会很有用。

用信号量的方式(极端情况允许线程数1)实现的互斥锁,没有谁占有谁释放,这种通过限制线程数量的锁是线程执行完之后就释放,其他等待线程进入

Semaphore的构造方法有两种:

第一种:

 

[java]  view plain  copy
 
  1. Semaphore(int permits) //用给定的许可数和非公平的公平设置创建一个 Semaphore。  

       第一种构造方法创建的信号灯,现在在获取的时候是随机的,没有一定的顺序,例如上例中,在前三个线程中的一个运行完毕以后,释放一个信号灯,剩下的两个线程就会随机的一个线程得到这个信号灯而运行。

第二种:

 

[java]  view plain  copy
 
  1. Semaphore(int permits, boolean fair) //用给定的许可数和给定的公平设置创建一个 Semaphore  


      The second constructor optionally accepts a fairness parameter. When set to false, this class makes no guarantees about the order in which threads acquire permissions. In particular, intrusion is allowed, which means that a thread calling acquire() can be assigned a permit before an already waiting thread, which logically means that the new thread puts itself at the head of the queue of waiting threads. When fairness is set to true, the semaphore guarantees that for any thread calling acquire() methods, threads are selected and granted in the order in which they are processed to call these methods (ie, first in, first out; FIFO). Note that FIFO ordering necessarily applies to specific internal execution points within these methods. So, it is possible that a thread called acquire() before another thread, but reached the sort point after that thread, and similarly when returning from the method. Also note that the non-synchronized tryAcquire() method does not use fairness settings, but rather any available permissions.
      In general, semaphores used to control resource access should be initialized to be fair to ensure that all threads have access to the resource. When using semaphores for other kinds of synchronization control, the throughput advantages of unfair ordering often outweigh fairness considerations.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326396940&siteId=291194637