线程高级应用-心得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 }
时间: 2024-12-28 18:15:13

线程高级应用-心得5-java5线程并发库中Lock和Condition实现线程同步通讯的相关文章

线程高级应用-心得4-java5线程并发库介绍,及新技术案例分析

1.  java5线程并发库新知识介绍 2.线程并发库案例分析 1 package com.itcast.family; 2 3 import java.util.concurrent.ExecutorService; 4 import java.util.concurrent.Executors; 5 import java.util.concurrent.TimeUnit; 6 7 public class ThreadPoolTest { 8 9 /** 10 * @param args

线程高级应用-心得8-java5线程并发库中同步集合Collections工具类的应用及案例分析

1.  HashSet与HashMap的联系与区别? 区别:前者是单列后者是双列,就是hashmap有键有值,hashset只有键: 联系:HashSet的底层就是HashMap,可以参考HashSet的类源码,默认构造方法为: public HashSet(){ map = new HashMap<key,Object> } 就是HashSet只用HashMap的键,而不用他的值,前者的值可以程序员随便指定,反正不用 2.  线程并发库中的集合优路劣之分 HashMap和HashSet如果在

Java中的线程--并发库中的集合

线程中的知识点基本都已经学完了,看看Java5并发库中提供的集合... 一.可堵塞队列 队列包含固定长度的队列和不固定长度的队列 ArrayBlockQueue中只有put()方法和take()方法才具有阻塞功能 1.阻塞队列的功能和效果,代码如下: 1 import java.util.concurrent.ArrayBlockingQueue; 2 import java.util.concurrent.BlockingQueue; 3 4 /** 5 * @className: Block

使用JUC并发工具包的Lock和Condition,实现生产者和消费者问题中的有界缓存

JDK5.0之前,用java实现生产者和消费者的唯一方式就是使用synchronized内置锁和wait/notify条件通知机制.JDK5.0之后提供了显示锁Lock和条件队列Condition,与内置锁和内置条件队列相对应,但是显示的锁和条件队列,功能更强大,更灵活.此外JDK5.0之后还提供了大量很有用的并发工具类,如BlockingQueue等,基于这些数据结构,能够方便.快速.高效的构建自己应用需要的效果.这里我们简单使用下显示锁和条件队列,来模拟有界缓存的实现,功能类似于JDK内置的

Java 并发编程中的 CyclicBarrier 用于一组线程互相等待

Java 5 引入的 Concurrent 并发库软件包中的 CyclicBarrier 是一个同步辅助类,它允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point).在涉及一组固定大小的线程的程序中,这些线程必须不时地互相等待,此时 CyclicBarrier 很有用.因为该 barrier 在释放等待线程后可以重用,所以称它为循环 的 barrier.CyclicBarrier 支持一个可选的 Runnable 命令,在一组线程中的最后一个线程到达之后(但在

线程高级应用-心得2-同步锁讲解及面试题案例分析

1.引入同步锁 2.同步锁案例分析 package com.itcast.family; /* * 传统线程的使用及注意事项 */ public class TraditionalThread { public static void main(String[] args) { //一.线程1:直接new一个thread子类,让子类run方法覆盖父类的run() Thread thread1 = new Thread(){ @Override public void run() { while(

据库中事务、会话、线程这几个概念是什么关系

1.会话可以创建多个事务比如:使用客端连接数据库,这样你就可以执行很多个事务了 2.一个事务只能由一个会话产生在数据库里的事务,如果在执行的SQL都是由会话发起的,哪怕是自动执行的JOB也是由系统会话发起的 3.一个事务可能会产生一个或多个线程比如RMAN备份,是可以创建多个线程可加快备份速度 4.一个线程在同一时间内只能执行一个事务而一个线程,在没结束当前事务是无法释放资源来执行第二个事务

Java复习——多线程与并发库

开启一个线程 实现一个线程的方式有两种:继承Thread类.实现Runnable接口.这两种方法都需要重写Run方法,具体的线程逻辑代码写在Run方法中.其实Thread类就实现了Runnable接口,但是并没有什么说法是使用哪种方式存在效率高低的问题,推荐使用实现Runnable接口的方式,因为更加面向对象,而且实现一个接口比继承一个类更灵活.我们可以使用匿名内部类的方式很方便的开启一个线程(使用Tread类的start方法开启一个线程): Thread : new Thread(){ pub

Android多线程研究(7)——Java5中的线程并发库

从这一篇开始我们将看看Java 5之后给我们添加的新的对线程操作的API,首先看看api文档: java.util.concurrent包含许多线程安全.测试良好.高性能的并发构建块,我们先看看atomic包下的AtomicInteger. import java.util.concurrent.atomic.AtomicInteger; public class AtomicIntegerTest { private static AtomicInteger data = new Atomic