zoukankan      html  css  js  c++  java
  • 线程高级应用-心得5-java5线程并发库中Lock和Condition实现线程同步通讯

    1.Lock相关知识介绍

       好比我同时种了几块地的麦子,然后就等待收割。收割时,则是哪块先熟了,先收割哪块。

       下面举一个面试题的例子来引出Lock缓存读写锁的案例,一个load()和get()方法返回值为空时的情况;load()的返回值是一个代理对象,而get()却是一个实实在在的对象;所以当返回对象为空是,get()返回null,load()返回一个异常对象;具体分析如下:

     一个读写锁的缓存库案例;用上面那道面试题分析则很好理解:

      线程阻塞问题:运用多个Condition对象解决

    2. Lock接口锁的使用

    Lock与synchronized最大区别就是:前者更面向对象;Lock要求程序员手动释放锁,synchronized自动释放

      1 package com.java5.thread.newSkill;
      2 
      3 //concurrent就是java5新增的线程并发库包
      4 import java.util.concurrent.locks.Lock;
      5 import java.util.concurrent.locks.ReentrantLock;
      6 
      7 /**
      8  * Lock接口锁的使用
      9  *   Lock与synchronized最大区别就是:前者更面向对象;
     10  *   Lock要求程序员手动释放锁,synchronized自动释放。
     11  */
     12 public class LockTest {
     13 
     14     public static void main(String[] args) {
     15         new LockTest().init();
     16 
     17     }
     18 
     19     // 该方法的作用是:外部类的静态方法不能实例化内部类对象;所以不能直接在外部类的main实例化,要创建一个中介的普通方法
     20     private void init() {
     21         final Outputer outputer = new Outputer();
     22         // 线程1
     23         new Thread(new Runnable() {
     24 
     25             @Override
     26             public void run() {
     27                 while (true) {
     28                     try {
     29                         Thread.sleep(10);
     30                     } catch (InterruptedException e) {
     31                         e.printStackTrace();
     32                     }
     33                     outputer.output("yangkai");
     34                 }
     35             }
     36         }).start();
     37         // 线程2
     38         new Thread(new Runnable() {
     39 
     40             @Override
     41             public void run() {
     42                 while (true) {
     43                     try {
     44                         Thread.sleep(10);
     45                     } catch (InterruptedException e) {
     46                         e.printStackTrace();
     47                     }
     48                     outputer.output("123456");
     49                 }
     50             }
     51         }).start();
     52     }
     53 
     54     static class Outputer {
     55         Lock lock = new ReentrantLock();
     56 
     57         public void output(String name) {
     58             //上锁
     59             lock.lock();
     60             try {
     61                 for (int i = 0; i < name.length(); i++) {
     62                     // 读取字符串内一个一个的字符
     63                     System.out.print(name.charAt(i));
     64                 }
     65                 System.out.println();
     66             } finally {
     67                 //释放锁;
     68                 /*这里放到finally里的原因是:万一上锁的这个方法中有异常发生;
     69                  * 那就不执行释放锁代码了,也就是成死锁了;好比你上厕所晕里面了;
     70                  * 后面的人等啊等的永远进不去似的
     71                  */
     72                 lock.unlock();
     73             }
     74         }
     75 
     76     }
     77 
     78     /*
     79      * 如果不使用线程锁Lock会出现以下情况: yangkai 123456 yangkai 1y2a3n4g5k6 ai
     80      */
     81 }
     82 3、读写锁的案例
     83 package com.java5.thread.newSkill;
     84 
     85 import java.util.Random;
     86 import java.util.concurrent.locks.ReadWriteLock;
     87 import java.util.concurrent.locks.ReentrantReadWriteLock;
     88 
     89 /**
     90  * 读写锁的案例
     91  */
     92 public class ReadWriteLockTest {
     93 
     94     public static void main(String[] args) {
     95 
     96         final Queues queues = new Queues();
     97         for ( int i = 0; i < 10; i++) {
     98             final int j = i;
     99             new Thread() {
    100                 public void run() {
    101                     //此处打标记A,下面注释会提到
    102                     /*if(j<10)*/ while(true){
    103                         queues.get();
    104                     }
    105                 }
    106             }.start();
    107             new Thread() {
    108                 public void run() {
    109                     /*if(j<10)*/while(true) {
    110                         queues.put(new Random().nextInt(10000));
    111                     }
    112                 }
    113             }.start();
    114         }
    115     }
    116 }
    117 
    118 class Queues {
    119     // 共享数据;只能有一个线程能写改数据,但能有多个线程同时读数据
    120     private Object data = null;
    121     /*这里如果这么写:
    122      *   ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
    123           上面的标记A处,如果用while(true)会一直写不读,但是如果不用while死循环则可以正确执行,
    124           比如用if(i<10);目前还没找到原因,希望大牛们看到后指点迷津;
    125          个人猜测:没有用面向接口编程,上锁后,死循环中的内容会无穷之的执行,执行不完不会开锁
    126     */
    127     ReadWriteLock rwl = new ReentrantReadWriteLock();
    128 
    129     // 读的方法,用的是读锁readLock()
    130     public void get() {
    131         rwl.readLock().lock();
    132         try {
    133             System.out.println(Thread.currentThread().getName()
    134                     + " be ready to read data!");
    135             Thread.sleep((long) Math.random() * 1000);
    136             System.out.println(Thread.currentThread().getName()
    137                     + " have read data:" + data);
    138         } catch (InterruptedException e) {
    139             e.printStackTrace();
    140         } finally {
    141             rwl.readLock().unlock();
    142         }
    143     }
    144 
    145     // 写的方法;用到写的锁:writeLock()
    146     public void put(Object data) {
    147         rwl.writeLock().lock();
    148         try {
    149             System.out.println(Thread.currentThread().getName()
    150                     + " be ready to write data!");
    151             Thread.sleep((long) Math.random() * 1000);
    152             this.data = data;
    153             System.out.println(Thread.currentThread().getName()
    154                     + " have write data:" + data);
    155         } catch (InterruptedException e) {
    156             e.printStackTrace();
    157         } finally {
    158             rwl.writeLock().unlock();
    159         }
    160     }
    161 }
    162 4. 缓存系统的模拟编写;读写锁的实际应用价值
    163 package com.java5.thread.newSkill;
    164 
    165 import java.util.HashMap;
    166 import java.util.Map;
    167 import java.util.concurrent.locks.ReadWriteLock;
    168 import java.util.concurrent.locks.ReentrantReadWriteLock;
    169 
    170 public class CacheDemo {
    171 
    172     /**
    173      * 缓存系统的模拟编写;读写锁的实际应用价值
    174      */
    175     private Map<String, Object> cache = new HashMap<String, Object>();
    176 
    177     public static void main(String[] args) {
    178 
    179     }
    180 
    181     private ReadWriteLock rwl = new ReentrantReadWriteLock();
    182 
    183     public Object getData(String key) {
    184         //如果客户一来读取value数据,则在客户一进去后上一把读锁;防止其他客户再次进行读,产生并发问题
    185         rwl.readLock().lock();
    186         Object value = null;
    187         try {
    188             value = cache.get(key);
    189             if (value == null) {
    190                 //如果如果读到的值为空则释放读锁,打开写锁,准备给value赋值
    191                 rwl.readLock().unlock();
    192                 rwl.writeLock().lock();
    193                 try {
    194                     //如果打开写锁还为空,则给value赋值aaa
    195                     if (value == null) {
    196                         value = "aaa";  //实际失去queryDB()
    197                     }
    198                 } finally {
    199                     //使用完写锁后关掉
    200                     rwl.writeLock().unlock();
    201                 }
    202                 //释放写锁后,再次打开读锁,供客户读取value的数据
    203                 rwl.readLock().lock();
    204             }
    205         } finally {
    206             //最后客户一读完后释放掉读锁
    207             rwl.readLock().unlock();
    208         }
    209         return value;
    210     }
    211 }
    212 5.新技术condition案例分析;代替wait()和notify()方法
    213 package com.java5.thread.newSkill;
    214 
    215 import java.util.concurrent.locks.Condition;
    216 import java.util.concurrent.locks.Lock;
    217 import java.util.concurrent.locks.ReentrantLock;
    218 
    219 /**
    220  * 面试题: 子线程循环10次,接着主线程循环100次,接着又回到子线程循环10次, 接着再回到主线程又循环100次,如此循环50次,代码如下:
    221  * 
    222  * 思路: 编写一个业务类,是为了不把自己绕进去,体现代码的的高聚类特性和代码的健壮性,
    223  * 即共同数据(比如这里的同步锁)或共同算法的若干个方法都可以提到同一个类中编写
    224  * 
    225  * 注意:wait()和notify()必须在synchronized关键字内使用;
    226  * 因为this.watit()中用到的this是synchronized()括号内的
    227  * 内容;如果不使用synchronized直接就用wait()会包状态不对的错误
    228  */
    229 public class ConditionCommunication {
    230 
    231     public static void main(String[] args) {
    232         final Business business = new Business();
    233         new Thread(new Runnable() {
    234 
    235             @Override
    236             public void run() {
    237                 for (int i = 1; i <= 50; i++) {
    238                     business.sub(i);
    239                 }
    240             }
    241         }).start();
    242 
    243         for (int i = 1; i <= 50; i++) {
    244             business.main(i);
    245         }
    246 
    247     }
    248 }
    249 
    250 // 编写一个有子方法(用来调用子线程)和主方法(调用主线程)的业务类
    251 
    252 class Business {
    253     private boolean bShouldSub = true;
    254     Lock lock = new ReentrantLock();
    255     // condition必须基于lock锁之上的
    256     Condition condition = lock.newCondition();
    257 
    258     public void sub(int i) {
    259         try {
    260             lock.lock();
    261             while (!bShouldSub) {
    262                 try {
    263                     // this.wait();
    264                     /*
    265                      * condition使用的是await()注意与wait()的区别;
    266                      * 因为condition也是Object对象所以也可以调用wait()方法,所以千万别调错了
    267                      */
    268                     condition.await();
    269                 } catch (Exception e) {
    270                     e.printStackTrace();
    271                 }
    272             }
    273             for (int j = 1; j <= 10; j++) {
    274                 System.out.println("sub thread sequence of  " + j
    275                         + " ,loop of  " + i);
    276             }
    277             bShouldSub = false;
    278             // this.notify();
    279             condition.signal();
    280         } finally {
    281             lock.unlock();
    282         }
    283     }
    284 
    285     public void main(int i) {
    286         /*
    287          * 这里最好用while,但是跟if的效果一样,只是前者代码更健壮, while可以防止线程自己唤醒自己,即通常所说的伪唤醒;
    288          * 相当于一个人做梦不是被别人叫醒而是自己做噩梦突然惊醒; 这时用while可以防止这种情况发生
    289          */
    290         try {
    291             lock.lock();
    292             while (bShouldSub) {
    293                 try {
    294                     // this.wait();
    295                     condition.await();
    296                 } catch (Exception e) {
    297                     e.printStackTrace();
    298                 }
    299             }
    300             for (int j = 1; j <= 100; j++) {
    301                 System.out.println("main thread sequence of  " + j
    302                         + " ,loop of  " + i);
    303             }
    304             bShouldSub = true;
    305             // this.notify();
    306             condition.signal();
    307         } finally {
    308             lock.unlock();
    309         }
    310     }
    311 }
    312 6. 多个Condition的应用场景;以下是三个condition通讯的代码:
    313 package com.java5.thread.newSkill;
    314 
    315 import java.util.concurrent.locks.Condition;
    316 import java.util.concurrent.locks.Lock;
    317 import java.util.concurrent.locks.ReentrantLock;
    318 
    319 /**
    320  * 多个Condition的应用场景
    321  * 以下是三个condition通讯的代码:
    322  */
    323 public class ThreeConditionCommunication {
    324 
    325     public static void main(String[] args) {
    326         final Business business = new Business();
    327         //线程2,老二线程
    328         new Thread(new Runnable() {
    329 
    330             @Override
    331             public void run() {
    332                 for (int i = 1; i <= 50; i++) {
    333                     business.sub(i);
    334                 }
    335             }
    336         }).start();
    337         
    338         //线程3,老三线程
    339         new Thread(new Runnable() {
    340             
    341             @Override
    342             public void run() {
    343                 for (int i = 1; i <= 50; i++) {
    344                     business.sub2(i);
    345                 }
    346             }
    347         }).start();
    348 
    349         //主线程1,老大线程
    350         for (int i = 1; i <= 50; i++) {
    351             business.main(i);
    352         }
    353 
    354     }
    355     
    356     /*编写一个有子方法(用来调用子线程)和主方法(调用主线程)的业务类
    357      * 这个项目下虽然有两个Business类;但是在不同包下所以不影响; 如果在同一包下,那么就要改名或者将其弄成内部类,如果又想要把他当外部类使用,
    358      * 那么将其弄成static 静态的就可以了
    359      */
    360     static class Business {
    361         private int shouldSub = 1;
    362         Lock lock = new ReentrantLock();
    363 
    364         Condition condition1 = lock.newCondition();
    365         Condition condition2 = lock.newCondition();
    366         Condition condition3 = lock.newCondition();
    367 
    368         public void sub(int i) {
    369             try {
    370                 lock.lock();
    371                 while (shouldSub != 2) {
    372                     try {                    
    373                         condition2.await();
    374                     } catch (Exception e) {
    375                         e.printStackTrace();
    376                     }
    377                 }
    378                 for (int j = 1; j <= 10; j++) {
    379                     System.out.println("sub thread sequence of  " + j
    380                             + " ,loop of  " + i);
    381                 }
    382                 shouldSub = 3;
    383                 condition3.signal();
    384             } finally {
    385                 lock.unlock();
    386             }
    387         }
    388         public void sub2(int i) {
    389             try {
    390                 lock.lock();
    391                 while (shouldSub != 3) {
    392                     try {                    
    393                         condition3.await();
    394                     } catch (Exception e) {
    395                         e.printStackTrace();
    396                     }
    397                 }
    398                 for (int j = 1; j <= 10; j++) {
    399                     System.out.println("sub2 thread sequence of  " + j
    400                             + " ,loop of  " + i);
    401                 }
    402                 shouldSub = 1;
    403                 condition1.signal();
    404             } finally {
    405                 lock.unlock();
    406             }
    407         }
    408 
    409         public void main(int i) {
    410             try {
    411                 lock.lock();
    412                 while (shouldSub != 1) {
    413                     try {;
    414                         condition1.await();
    415                     } catch (Exception e) {
    416                         e.printStackTrace();
    417                     }
    418                 }
    419                 for (int j = 1; j <= 100; j++) {
    420                     System.out.println("main thread sequence of  " + j
    421                             + " ,loop of  " + i);
    422                 }
    423                 shouldSub = 2;
    424                 condition2.signal();
    425             } finally {
    426                 lock.unlock();
    427             }
    428         }
    429     }
    430 }
  • 相关阅读:
    改进昨天的电梯电梯问题
    电梯调度
    结对开发四---实现环状二维数组求最大子数组的和
    结对开发五--对一千个数long型的一维数组求最大子数组的和
    结对开发四------求一维无头数组最大子数组的和
    结对开发3--电梯调度问题需求分析
    SQL基础题
    类和对象(课后题)
    再战文件(小甲鱼课后题)python超级入门
    快来集合!python超级入门笔记
  • 原文地址:https://www.cnblogs.com/cxxjohnson/p/6261893.html
Copyright © 2011-2022 走看看