JAVA 并发-线程同步通信技术(Lock和Condition)(10)

在之前的博客中已经介绍过线程同步通信技术《JAVA 并发-多线程传统线程同步通信技术(4)》,上篇是使用的synchronized,waitnotify来实现,今天我们使用的是LockCondition,下面我们结合两者对比来学习。

 

简单的Lock锁应用:


[java]  view plain  copy
 print ?
  1. /** 
  2.  * 简单Lock的应用 
  3.  * @author hejingyuan 
  4.  * 
  5.  */  
  6. public class LockTest {  
  7.   
  8.     public static void main(String[] args) {  
  9.         new LockTest().init();  
  10.     }  
  11.       
  12.     private void init(){  
  13.         final Outputer outputer = new Outputer();  
  14.         new Thread(new Runnable(){  
  15.             @Override  
  16.             public void run() {  
  17.                 while(true){  
  18.                     try {  
  19.                         Thread.sleep(10);  
  20.                     } catch (InterruptedException e) {  
  21.                         e.printStackTrace();  
  22.                     }  
  23.                     outputer.output("zhangxiaoxiang");  
  24.                 }  
  25.                   
  26.             }  
  27.         }).start();  
  28.           
  29.         new Thread(new Runnable(){  
  30.             @Override  
  31.             public void run() {  
  32.                 while(true){  
  33.                     try {  
  34.                         Thread.sleep(10);  
  35.                     } catch (InterruptedException e) {  
  36.                         e.printStackTrace();  
  37.                     }  
  38.                     outputer.output("lihuoming");  
  39.                 }  
  40.                   
  41.             }  
  42.         }).start();  
  43.           
  44.     }  
  45.   
  46.     static class Outputer{  
  47.         Lock lock = new ReentrantLock();  
  48.         public void output(String name){  
  49.             int len = name.length();  
  50.             lock.lock();  
  51.             try{  
  52.                 for(int i=0;i<len;i++){  
  53.                     System.out.print(name.charAt(i));  
  54.                 }  
  55.                 System.out.println();  
  56.             }finally{  
  57.                 lock.unlock();  
  58.             }  
  59.         }  
  60.     }  
  61. }  


Lock比传统线程模型中的Synchronied方式更加面向对象,与生活中的锁类似,锁本身也应该是一个对象.两个线程执行的代码段要实现同步互斥的效果,它们必须用同一个Lock对象,锁是在代表要操作的资源的类的内部方法中,而不是线程代码中.

 

注意:

 

和synchronized不同的是,在线程执行完以后,要关闭锁unlock(),如果不关闭,其他在等待的线程就永远被锁在外面了。因为synchronized是在JVM层面实现的,因此系统可以监控锁的释放与否,而ReentrantLock使用代码实现的,系统无法自动释放锁,需要在代码中finally子句中显式释放锁lock.unlock();

 

 

结合前篇博客进行对比(同步通信):

 

实现效果:子线程循环10次,接着主线程循环100次,又回到子线程循环10次,接着再回到主线程又循环100次,如此循环50次


[java]  view plain  copy
 print ?
  1. public class ConditionCommunication {  
  2.   
  3.     public static void main(String[] args) {  
  4.           
  5.         final Business business = new Business();  
  6.         //创建了一个线程,并启动  
  7.         new Thread(  
  8.                 new Runnable() {  
  9.                       
  10.                     @Override  
  11.                     public void run() {                   
  12.                         for(int i=1;i<=50;i++){  
  13.                             business.sub(i);  
  14.                         }                         
  15.                     }  
  16.                 }  
  17.         ).start();  
  18.         //因为mian方法本身就占用一个线程,所以主线程不需要再new Thread  
  19.         for(int i=1;i<=50;i++){  
  20.             business.main(i);  
  21.         }  
  22.           
  23.     }  
  24.   
  25.     static class Business {  
  26.           
  27.           Lock lock = new ReentrantLock();  
  28.           Condition condition = lock.newCondition();  
  29.           //决定是main执行还是sub执行  
  30.           private boolean bShouldSub = true;  
  31.           public  void sub(int i){  
  32.               lock.lock();// 锁住了别的线程就不能进来了,包括下面的main()因为他们用的是同一把锁  
  33.               try{  
  34.                   //bShouldSub==false时等待  
  35.                   while(!bShouldSub){  
  36.                       try {  
  37.                         condition.await();  
  38.                     } catch (Exception e) {  
  39.                         e.printStackTrace();  
  40.                     }  
  41.                   }  
  42.                     for(int j=1;j<=10;j++){  
  43.                         System.out.println("sub thread sequence of " + j + ",loop of " + i);  
  44.                     }  
  45.                   bShouldSub = false;  
  46.                   condition.signal();  
  47.               }finally{  
  48.                   lock.unlock();  
  49.               }  
  50.           }  
  51.             
  52.           public  void main(int i){  
  53.               lock.lock();  
  54.               try{  
  55.                  //bShouldSub==true时等待  
  56.                  while(bShouldSub){  
  57.                         try {  
  58.                             condition.await();  
  59.                         } catch (Exception e) {  
  60.                             e.printStackTrace();  
  61.                         }  
  62.                     }  
  63.                     for(int j=1;j<=100;j++){  
  64.                         System.out.println("main thread sequence of " + j + ",loop of " + i);  
  65.                     }  
  66.                     bShouldSub = true;  
  67.                     condition.signal();  
  68.           }finally{  
  69.             //如果中途抛出异常,那么这把锁就没有被解锁,别人就进不来了  
  70.             //所以写在finally里面  
  71.               lock.unlock();  
  72.           }  
  73.       }  
  74.       
  75.     }  
  76. }  

在Condition中,用await()替换wait(),用signal()替换notify(),用signalAll()替换notifyAll(),传统线程的通信方式,Condition都可以实现。

Condition实例实质上被绑定到一个锁上。要为特定Lock实例获得Condition 实例,请使用其newCondition() 方法。

 

注意:在等待Condition时,可能会发生"虚假唤醒"。

 

虚假唤醒(spuriouswakeup)在采用条件等待时,我们使用的是

 

while(条件不满足){ 

   condition_wait(cond, mutex); 

而不是: 

If( 条件不满足 ){ 

   Condition_wait(cond,mutex); 

}  

 

这是因为可能会存在虚假唤醒”spuriouswakeup”的情况。

也就是说,即使没有线程调用condition_signal,原先调用condition_wait的函数也可能会返回。此时线程被唤醒了,但是条件并不满足,这个时候如果不对条件进行检查而往下执行,就可能会导致后续的处理出现错误。

 

在系统设计时应该可以避免虚假唤醒,但是这会影响条件变量的执行效率,而既然通过while循环就能避免虚假唤醒造成的错误,因此程序的逻辑就变成了while循环的情况。

 

Condition可以控制多个线程之间的运行顺序 


Condition和传统的线程通信没什么区别,Condition的强大之处在于它可以为多个线程间建立不同的Condition,这样可以控制多个线程之间的运行顺序。

 

例如:

 

应当先对线程1、线程2,建 condition对象 1: c_th1,对象 2: c_th2;

c_th1.await()   // 阻塞写线程1

c_th2.signal()   // 唤醒读线程2

-------

否则,多线程时,函数没有参数时,如何指定阻塞哪个,唤醒哪个呢,线程可能多于2个,总要有方法指定。

 

实现demo(有三个线程,想让线程1运行完以后运行线程2,线程2运行完以后运行线程3,线程3运行完以后又运行线程1):


[java]  view plain  copy
 print ?
  1. public class ThreeConditionCommunication {  
  2.   
  3.     public static void main(String[] args) {  
  4.           
  5.         final Business business = new Business();  
  6.         new Thread(  
  7.                 new Runnable() {  
  8.                       
  9.                     @Override  
  10.                     public void run() {  
  11.                       
  12.                         for(int i=1;i<=50;i++){  
  13.                             business.sub2(i);  
  14.                         }  
  15.                           
  16.                     }  
  17.                 }  
  18.         ).start();  
  19.           
  20.         new Thread(  
  21.                 new Runnable() {  
  22.                       
  23.                     @Override  
  24.                     public void run() {  
  25.                       
  26.                         for(int i=1;i<=50;i++){  
  27.                             business.sub3(i);  
  28.                         }  
  29.                           
  30.                     }  
  31.                 }  
  32.         ).start();        
  33.           
  34.         for(int i=1;i<=50;i++){  
  35.             business.main(i);  
  36.         }  
  37.           
  38.     }  
  39.   
  40.     static class Business {  
  41.           Lock lock = new ReentrantLock();  
  42.           Condition condition1 = lock.newCondition();  
  43.           Condition condition2 = lock.newCondition();  
  44.           Condition condition3 = lock.newCondition();  
  45.           private int shouldSub = 1;  
  46.           public  void sub2(int i){  
  47.               lock.lock();  
  48.               try{  
  49.                   while(shouldSub != 2){  
  50.                       try {  
  51.                         condition2.await();  
  52.                     } catch (Exception e) {  
  53.                         e.printStackTrace();  
  54.                     }  
  55.                   }  
  56.                     for(int j=1;j<=10;j++){  
  57.                         System.out.println("sub2 thread sequence of " + j + ",loop of " + i);  
  58.                     }  
  59.                   shouldSub = 3;  
  60.                   condition3.signal();  
  61.               }finally{  
  62.                   lock.unlock();  
  63.               }  
  64.           }  
  65.   
  66.           public  void sub3(int i){  
  67.               lock.lock();  
  68.               try{  
  69.                   while(shouldSub != 3){  
  70.                       try {  
  71.                         condition3.await();  
  72.                     } catch (Exception e) {  
  73.                         e.printStackTrace();  
  74.                     }  
  75.                   }  
  76.                     for(int j=1;j<=20;j++){  
  77.                         System.out.println("sub3 thread sequence of " + j + ",loop of " + i);  
  78.                     }  
  79.                   shouldSub = 1;  
  80.                   condition1.signal();  
  81.               }finally{  
  82.                   lock.unlock();  
  83.               }  
  84.           }         
  85.             
  86.           public  void main(int i){  
  87.               lock.lock();  
  88.               try{  
  89.                  while(shouldSub != 1){  
  90.                         try {  
  91.                             condition1.await();  
  92.                         } catch (Exception e) {  
  93.                             e.printStackTrace();  
  94.                         }  
  95.                     }  
  96.                     for(int j=1;j<=100;j++){  
  97.                         System.out.println("main thread sequence of " + j + ",loop of " + i);  
  98.                     }  
  99.                     shouldSub = 2;  
  100.                     condition2.signal();  
  101.           }finally{  
  102.               lock.unlock();  
  103.           }  
  104.       }  
  105.       
  106.     }  
  107. }  

总结:

 

对于以上的两种实现线程同步通信的方式,Lock替代了 synchronized 方法和语句的使用,Condition 替代了 Object 监视器方法的使用。而对比来说Lock(本身为一个对象)更加面向对象,Condition可以为多个线程间建立不同的Condition,这样可以控制多个线程之间的运行顺序。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值