-
wait()和notify()
wait会释放锁,notify不会释放锁。必须与synchronize使用。。。
1 /** 2 * 曾经的面试题:(淘宝?) 3 * 实现一个容器,提供两个方法,add,size 4 * 写两个线程,线程1添加10个元素到容器中,线程2实现监控元素的个数,当个数到5个时,线程2给出提示并结束 5 * 6 * 给lists添加volatile之后,t2能够接到通知,但是,t2线程的死循环很浪费cpu,如果不用死循环,该怎么做呢? 7 * 8 * 这里使用wait和notify做到,wait会释放锁,而notify不会释放锁 9 * 需要注意的是,运用这种方法,必须要保证t2先执行,也就是首先让t2监听才可以 10 * 14 * 15 * notify之后,t1必须释放锁,t2退出后,也必须notify,通知t1继续执行 16 * 整个通信过程比较繁琐 17 * @author mashibing 18 */ 19 package com.concurrent.c019; 20 21 import java.util.ArrayList; 22 import java.util.List; 23 import java.util.concurrent.TimeUnit; 24 25 26 public class MyContainer4 { 27 28 //添加volatile,使t2能够得到通知 29 volatile List lists = new ArrayList(); 30 31 public void add(Object o) { 32 lists.add(o); 33 } 34 35 public int size() { 36 return lists.size(); 37 } 38 39 public static void main(String[] args) { 40 MyContainer4 c = new MyContainer4(); 41 42 final Object lock = new Object(); 43 44 new Thread(() -> { 45 synchronized(lock) { 46 System.out.println("t2启动"); 47 if(c.size() != 5) { 48 try { 49 lock.wait(); 50 } catch (InterruptedException e) { 51 e.printStackTrace(); 52 } 53 } 54 System.out.println("t2 结束"); 55 //通知t1继续执行 56 lock.notify(); 57 } 58 59 }, "t2").start(); 60 61 try { 62 TimeUnit.SECONDS.sleep(1); 63 } catch (InterruptedException e1) { 64 e1.printStackTrace(); 65 } 66 67 new Thread(() -> { 68 System.out.println("t1启动"); 69 synchronized(lock) { 70 for(int i=0; i<10; i++) { 71 c.add(new Object()); 72 System.out.println("add " + i); 73 74 if(c.size() == 5) { 75 lock.notify(); 76 //释放锁,让t2得以执行 77 try { 78 lock.wait(); 79 } catch (InterruptedException e) { 80 e.printStackTrace(); 81 } 82 } 83 84 try { 85 TimeUnit.SECONDS.sleep(1); 86 } catch (InterruptedException e) { 87 e.printStackTrace(); 88 } 89 } 90 } 91 }, "t1").start(); 92 93 94 } 95 }
==============================
另一种使用CountDown
1 /** 2 * 曾经的面试题:(淘宝?) 3 * 实现一个容器,提供两个方法,add,size 4 * 写两个线程,线程1添加10个元素到容器中,线程2实现监控元素的个数,当个数到5个时,线程2给出提示并结束 5 * 6 * 给lists添加volatile之后,t2能够接到通知,但是,t2线程的死循环很浪费cpu,如果不用死循环,该怎么做呢? 7 * 8 * 这里使用wait和notify做到,wait会释放锁,而notify不会释放锁 9 * 需要注意的是,运用这种方法,必须要保证t2先执行,也就是首先让t2监听才可以 10 * 11 * 阅读下面的程序,并分析输出结果 12 * 可以读到输出结果并不是size=5时t2退出,而是t1结束时t2才接收到通知而退出 13 * 想想这是为什么? 14 * 15 * notify之后,t1必须释放锁,t2退出后,也必须notify,通知t1继续执行 16 * 整个通信过程比较繁琐 17 * 18 * 使用Latch(门闩)替代wait notify来进行通知 19 * 好处是通信方式简单,同时也可以指定等待时间 20 * 使用await和countdown方法替代wait和notify 21 * CountDownLatch不涉及锁定,当count的值为零时当前线程继续运行 22 * 当不涉及同步,只是涉及线程通信的时候,用synchronized + wait/notify就显得太重了 23 * 这时应该考虑countdownlatch/cyclicbarrier/semaphore 24 * @author mashibing 25 */ 26 package com.concurrent.c019; 27 28 import java.util.ArrayList; 29 import java.util.List; 30 import java.util.concurrent.CountDownLatch; 31 import java.util.concurrent.TimeUnit; 32 33 public class MyContainer5 { 34 35 // 添加volatile,使t2能够得到通知 36 volatile List lists = new ArrayList(); 37 38 public void add(Object o) { 39 lists.add(o); 40 } 41 42 public int size() { 43 return lists.size(); 44 } 45 46 public static void main(String[] args) { 47 MyContainer5 c = new MyContainer5(); 48 49 CountDownLatch latch = new CountDownLatch(1); 50 51 new Thread(() -> { 52 System.out.println("t2启动"); 53 if (c.size() != 5) { 54 try { 55 latch.await(); 56 57 //也可以指定等待时间 58 //latch.await(5000, TimeUnit.MILLISECONDS); 59 } catch (InterruptedException e) { 60 e.printStackTrace(); 61 } 62 } 63 System.out.println("t2 结束"); 64 65 }, "t2").start(); 66 67 try { 68 TimeUnit.SECONDS.sleep(1); 69 } catch (InterruptedException e1) { 70 e1.printStackTrace(); 71 } 72 73 new Thread(() -> { 74 System.out.println("t1启动"); 75 for (int i = 0; i < 10; i++) { 76 c.add(new Object()); 77 System.out.println("add " + i); 78 79 if (c.size() == 5) { 80 // 打开门闩,让t2得以执行 81 latch.countDown(); 82 } 83 84 try { 85 TimeUnit.SECONDS.sleep(1); 86 } catch (InterruptedException e) { 87 e.printStackTrace(); 88 } 89 } 90 91 }, "t1").start(); 92 93 } 94 }