线程高级应用-心得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 }
原文地址:https://www.cnblogs.com/cxxjohnson/p/6261893.html