并发编程之wait()、notify()
前面的并发编程之volatile中我们用程序模拟了一个场景:在main方法中开启两个线程,其中一个线程t1往list里循环添加元素,另一个线程t2监听list中的size,当size等于5时,t2线程结束,t1线程继续执行,直到循环结束,上篇文章是用volatile来保证内存的可见性,从而访问共享内存来实现两个线程之间的通信,这篇文章我们用wait()和notify()来实现此功能。我们先来看看以下代码是否满足要求:
1 package com.fanjf.thread; 2 3 import java.util.ArrayList; 4 import java.util.List; 5 import java.util.concurrent.TimeUnit; 6 7 public class Mycontainer2 { 8 static List<Integer> integers = new ArrayList<>(); 9 final static Object object = new Object(); 10 11 public static void main(String[] args) { 12 new Thread(new Runnable() { 13 public void run() { 14 System.out.println("t1启动"); 15 try { 16 TimeUnit.SECONDS.sleep(1); 17 } catch (InterruptedException e1) { 18 e1.printStackTrace(); 19 } 20 synchronized (object) { 21 for(int i=0;i<10;i++){ 22 integers.add(i); 23 if(integers.size()==5){ 24 object.notify(); 25 } 26 try { 27 Thread.sleep(1000); 28 } catch (InterruptedException e) { 29 e.printStackTrace(); 30 } 31 System.out.println("add:"+i); 32 } 33 34 } 35 System.out.println("t1结束"); 36 37 } 38 }).start(); 39 40 new Thread(new Runnable() { 41 public void run() { 42 System.out.println("t2启动"); 43 synchronized (object) { 44 try { 45 object.wait(); 46 } catch (InterruptedException e) { 47 e.printStackTrace(); 48 } 49 50 } 51 System.out.println("t2结束"); 52 53 54 } 55 56 }).start(); 57 } 58 59 }
这段程序看着好像是没什么问题:线程t1启动,休眠1s以便让t2获得锁执行wait方法,当list的size不等于5时,t1线程执行add方法,t2线程等待,当size等于5时,t1线程通知t2线程继续执行直到t2结束再继续执行t1,下面我们看下执行结果是否跟我们预期的一样:
1 t1启动 2 t2启动 3 add:0 4 add:1 5 add:2 6 add:3 7 add:4 8 add:5 9 add:6 10 add:7 11 add:8 12 add:9 13 t1结束 14 t2结束
从结果可以看出,t2并没有立马让出线程,而是一直运行到t1结束,这里有一个非常关键的点是:notify()方法不会立即释放锁,而是要等到t1线程执行完毕才会释放锁,所以才有了上面add方法一直运行到t1结束,我们要想让t1在size等于5时让出线程给t2,光用notify()是不行的,我们应该在notify()之后紧跟着执行wait(),通过wait()来释放锁,这样t2就能执行打印结束的语句了。仔细想一想,这样就可以了吗?当t1执行wait()方法让出线程给t2之后,t2执行完毕,此时t1还是处于等待状态,执行wait()方法的线程必须要等待获得同一把锁的其他线程执行notify()方法以后此线程才能重新获得锁继续执行,这样的话此时t1线程永远都得不到执行了,所以我们应该在t2线程结束之前执行notify()让t1重新获得锁继续执行add操作,直到线程结束。改造后的代码如下:红色部分为新加部分
1 package com.fanjf.thread; 2 3 import java.util.ArrayList; 4 import java.util.List; 5 import java.util.concurrent.TimeUnit; 6 7 public class Mycontainer2 { 8 static List<Integer> integers = new ArrayList<>(); 9 final static Object object = new Object(); 10 11 public static void main(String[] args) { 12 new Thread(new Runnable() { 13 public void run() { 14 System.out.println("t1启动"); 15 try { 16 TimeUnit.SECONDS.sleep(1); 17 } catch (InterruptedException e1) { 18 e1.printStackTrace(); 19 } 20 synchronized (object) { 21 for(int i=0;i<10;i++){ 22 integers.add(i); 23 if(integers.size()==5){ 24 object.notify(); 25 try { 26 object.wait(); 27 } catch (InterruptedException e) { 28 e.printStackTrace(); 29 } 30 } 31 try { 32 Thread.sleep(1000); 33 } catch (InterruptedException e) { 34 e.printStackTrace(); 35 } 36 System.out.println("add:"+i); 37 } 38 39 } 40 System.out.println("t1结束"); 41 42 } 43 }).start(); 44 45 new Thread(new Runnable() { 46 public void run() { 47 System.out.println("t2启动"); 48 synchronized (object) { 49 try { 50 object.wait(); 51 } catch (InterruptedException e) { 52 e.printStackTrace(); 53 } 54 object.notify(); 55 56 } 57 System.out.println("t2结束"); 58 59 60 } 61 62 }).start(); 63 } 64 65 }
运行结果如下:
1 t1启动 2 t2启动 3 add:0 4 add:1 5 add:2 6 add:3 7 t2结束 8 add:4 9 add:5 10 add:6 11 add:7 12 add:8 13 add:9 14 t1结束
此时程序运行的结果就正确了,其实,只要理解了wait()会立即释放锁,notify()不会立即释放锁,上面的代码就很容易理解了!
                    
                
                
            
        
浙公网安备 33010602011771号