java线程并发之信号量(Semaphore)

作者: 扈扈哈嘿 | 来源:发表于2017-01-04 17:41 被阅读3599次

    前面说到过锁和synchronized,现在说的同步机制信号量(Semaphore)是个什么概念呢。又有什么区别呢?
     Lock和synchronized是锁的互斥,一个线程如果锁定了一资源,那么其它线程只能等待资源的释放。也就是一次只有一个线程执行,这到这个线程执行完毕或者unlock。而Semaphore可以控制多个线程同时对某个资源的访问。Semaphore实现的功能就类似厕所有5个坑,假如有10个人要上厕所,那么同时只能有多少个人去上厕所呢?同时只能有5个人能够占用,当5个人中 的任何一个人让开后,其中等待的另外5个人中又有一个人可以占用了。另外等待的5个人中可以是随机获得优先机会,也可以是按照先来后到的顺序获得机会,这取决于构造Semaphore对象时传入的参数选项。当然单个信号量的Semaphore对象可以实现互斥锁的功能,并且可以是由一个线程获得了“锁”,再由另一个线程释放“锁”,这可应用于死锁恢复的一些场合。
     信号量用在多线程多任务同步的,一个线程完成了某一个动作就通过信号量告诉别的线程,别的线程再进行某些动作。也就是说Semaphore不一定是锁定某个资源,而是流程上的概念。比方说有A,B两个线程,B线程的操作可能要等A线程执行完毕之后才执行,这个任务 并不一定是锁定某一资源,还可以是进行一些计算或者数据处理之类,它们也许并不访问共享变量,只是逻辑上的先后顺序。
     java中计数信号量(Semaphore)维护着一个许可集。调用acquire()获取一个许可,release()释放一个许可。 在java中,还可以设置该信号量是否采用公平模式,如果以公平方式执行,则线程将会按到达的顺序(FIFO)执行,如果是非公平,则可以后请求的有可能排在队列的头部。Semaphore当前在多线程环境下被扩放使用,操作系统的信号量是个很重要的概念,在进程控制方面都有应用。Java并发库Semaphore 可以很轻松完成信号量控制,Semaphore可以控制某个资源可被同时访问的个数。
    下面是一个代码例子:

    public static void main(String[] args) {   
     ExecutorService executorService = Executors.newCachedThreadPool();  
      final Semaphore semaphore = new Semaphore(5);
        for (int index = 0; index < 20; index++) {  
          final int NO = index;      
      Runnable runnable = new Runnable() {   
             @Override        
        public void run() {  
                  try {       
                 semaphore.acquire();        
                System.out.println("Accessing:" + NO);        
                Thread.sleep((long) (Math.random() * 10000));        
                semaphore.release();             
              System.out.println("------------------" + semaphore.availablePermits());   
                 } catch (InterruptedException e) {            
            e.printStackTrace();                }  
              }      
      };      
      executorService.execute(runnable);   
     }    executorService.shutdown();
    }
    

    我们现在知道线程同步的方式有synchronized,volatile,Lock,Semaphore等等。

    使用信号量解决死锁问题

    public class BadLockTest {   
     protected Object obj1 = new Object();
     protected Object obj2 = new Object();  
     protected ExecutorService executorService = Executors.newCachedThreadPool();   
     protected Task1 test1=new Task1();   
     protected Task2 test2=new Task2();  
      public static void main(String[] args) {    
        BadLockTest test = new BadLockTest();      
      for(int i=0;i<50;i++){         
       test.test1.setCount(i);     
       test.test2.setCount(i);          
       test.executorService.execute(test.test1);            test.executorService.execute(test.test2);  
          }    }  
      class Task1 implements Runnable {  
       public int count;     
       public void setCount(int count){        
       this.count=count;        }   
         @Override    
        public void run() {      
        synchronized (obj1) {         
        System.out.println("task1得到obj1对象锁"+count);           
         synchronized (obj2) {            
            System.out.println("task1得到obj2对象锁"+count);    
                }       
         }     
       }    }   
     class Task2 implements Runnable {      
      public int count;     
       public void setCount(int count){     
           this.count=count;     
       }       
     @Override     
       public void run() {      
          synchronized (obj2) {    
          System.out.println("task1得到obj1对象锁"+count);       
           synchronized (obj1) {            
            System.out.println("task1得到obj2对象锁"+count);          
          }          
      }    
        }  
      }}
    

    得到结果:

    task1得到obj1对象锁1
    task1得到obj1对象锁1
    

    可从结果就知道已经发生了死锁。
     信号量可以控制资源能被多少线程访问,这里我们指定只能被一个线程访问,就做到了类似锁住。而信号量可以指定去获取的超时时间,我们可以根据这个超时时间,去做一个额外处理。
     对于无法成功获取的情况,一般就是重复尝试,或指定尝试的次数,也可以马上退出。

    public class BadLockTest {   
      protected ExecutorService executorService = Executors.newCachedThreadPool();   
     protected Task1 test1=new Task1();  
      protected Task2 test2=new Task2();  
      protected Semaphore s1=new Semaphore(1);  
      protected Semaphore s2=new Semaphore(1);   
     public static void main(String[] args) {     
      BadLockTest test = new BadLockTest();    
        for(int i=0;i<50;i++){      
          test.test1.setCount(i);     
           test.test2.setCount(i);       
         test.executorService.execute(test.test1);     
           test.executorService.execute(test.test2);    
        }  
      }   
     class Task1 implements Runnable {   
         public int count;   
         public void setCount(int count){     
         this.count=count;        }    
        @Override     
       public void run() {      
          try {        
          if(s2.tryAcquire(1, TimeUnit.SECONDS)){    
                    System.out.println("task1得到obj1对象锁"+count);     
                   if(s1.tryAcquire(1, TimeUnit.SECONDS)){    
                        System.out.println("task1得到obj2对象锁"+count);       
                 }            
        }           
         s2.release();        
        s1.release();       
         } catch (InterruptedException e) {      
              e.printStackTrace();         
       }      
      }   
     }   
     class Task2 implements Runnable {     
       public int count;    
       public void setCount(int count){         
       this.count=count;        }    
        @Override     
       public void run() {       
       //  synchronized (obj2) {     
           try {          
          if(s1.tryAcquire(1, TimeUnit.SECONDS)){       
            System.out.println("task2得到obj1对象锁"+count);  
           if(s2.tryAcquire(1, TimeUnit.SECONDS)){        
               System.out.println("task2得到obj2对象锁"+count);          
              }        
           }      
              s1.release();          
          s2.release();      
          } catch (InterruptedException e) {      
              e.printStackTrace();      
          }     
       }  
      }}
    

    结果:

    task1得到obj2对象锁49
    task2得到obj2对象锁49
    

    相关文章

      网友评论

        本文标题:java线程并发之信号量(Semaphore)

        本文链接:https://www.haomeiwen.com/subject/nogovttx.html