并发编程之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()不会立即释放锁,上面的代码就很容易理解了!

 

posted @ 2019-04-19 20:33  飞非的博客  阅读(313)  评论(0)    收藏  举报