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 }
  • 相关阅读:
    Java实现 蓝桥杯VIP 算法训练 字符串逆序
    Java实现 蓝桥杯VIP 算法训练 字符串逆序
    Java实现 蓝桥杯VIP 算法训练 最长字符串
    Java实现 蓝桥杯VIP 算法训练 最长字符串
    Java实现 蓝桥杯VIP 算法训练 最长字符串
    Java实现 蓝桥杯VIP 算法训练 最长字符串
    Java实现 蓝桥杯VIP 算法训练 最长字符串
    Java实现 蓝桥杯VIP 算法训练 成绩的等级输出
    Java实现 蓝桥杯VIP 算法训练 成绩的等级输出
    Qt 自定义model实现文件系统的文件名排序
  • 原文地址:https://www.cnblogs.com/cxxjohnson/p/6261893.html
Copyright © 2011-2022 走看看