Java多线程之wait/notify/notifyAll

 1 import java.util.ArrayList;
 2 import java.util.List;
 3 import java.util.concurrent.TimeUnit;
 4 
 5 /**
 6  * 创建一个容器,一根线程往里放东西,一根线程判断是否符合条件
 7  */
 8 public class MyContainer2 {
 9 
10     /*volatile*/ List<Object> list = new ArrayList<>();  //内存不可见
11 
12     void add(Object o) {
13         try {
14             TimeUnit.SECONDS.sleep(1);
15         } catch (InterruptedException e) {
16             e.printStackTrace();
17         }
18         list.add(o);
19         System.out.println(Thread.currentThread().getName() + " add:" + o);
20     }
21 
22     int getSize() {
23         return this.list.size();
24     }
25 
26     public static void main(String[] args) {
27 
28         MyContainer2 container = new MyContainer2();
29 
30         new Thread(() -> {
31             for(int i=0; i<10; i++) {
32                 container.add("Object" + i);
33             }
34         }, "线程1").start();
35 
36         new Thread(() -> {
37             while (true) { //很浪费cpu资源
38                 if(container.getSize() > 5) break;
39             }
40             System.out.println(Thread.currentThread().getName() + "结束");
41         }, "线程2").start();
42     }
43 }

看明白接着看,上面代码while(true)很消耗cpu资源,下面代码用到wait和notify,阻塞线程和唤醒线程:

 1 import java.util.ArrayList;
 2 import java.util.List;
 3 import java.util.concurrent.TimeUnit;
 4 
 5 /**
 6  * wait 线程会失去锁
 7  *
 8  * notify/notifyAll 叫醒线程 不会释放锁
 9  *
10  */
11 public class MyContainer3 {
12 
13     volatile List<Object> list = new ArrayList<>();
14 
15     void add(Object o) {
16         try {
17             TimeUnit.SECONDS.sleep(1);
18         } catch (InterruptedException e) {
19             e.printStackTrace();
20         }
21         list.add(o);
22         System.out.println(Thread.currentThread().getName() + " add:" + o);
23     }
24 
25     int getSize() {
26         return this.list.size();
27     }
28 
29     public static void main(String[] args) {
30 
31         MyContainer3 container = new MyContainer3();
32 
33         final Object block = new Object(); //
34 
35         new Thread(() -> {
36             synchronized (block) {
37                 System.out.println(Thread.currentThread().getName() + "开始");
38                 if(container.getSize() < 5) {
39                     try {
40                         System.out.println(Thread.currentThread().getName() + "阻塞");
41                         block.wait();
42                     } catch (InterruptedException e) {
43                         e.printStackTrace();
44                     }
45                 }
46                 System.out.println(Thread.currentThread().getName() + "结束");
47             }
48         }, "线程2").start();
49 
50         new Thread(() -> {
51             synchronized (block) {
52                 for(int i=0; i<10; i++) {
53                     container.add("Object" + i);
54 
55                     if(i > 5) {
56                         block.notify(); //当唤醒在block上等待的线程后,线程1并不会失去锁,需要等线程1执行完毕释放锁,线程2才能接着执行。
57                     }
58 
59                     try {
60                         TimeUnit.SECONDS.sleep(1);
61                     } catch (InterruptedException e) {
62                         e.printStackTrace();
63                     }
64                 }
65             }
66         }, "线程1").start();
67 
68     }
69 
70 }

看明白接着看,上面线程执行效果不理想,下面进行了修改:

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * wait 失去锁
 *
 * notify/notifyAll 叫醒线程 不会释放锁
 *
 * wait和notify/notifyAll要在synchronized代码块里
 *
 * MyContainer3 改良版
 *
 */
public class MyContainer4 {

    volatile List<Object> list = new ArrayList<>();

    void add(Object o) {
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        list.add(o);
        System.out.println(Thread.currentThread().getName() + " add:" + o);
    }

    int getSize() {
        return this.list.size();
    }

    public static void main(String[] args) {

        MyContainer4 container = new MyContainer4();

        final Object block = new Object(); //

        new Thread(() -> {
            synchronized (block) {
                System.out.println(Thread.currentThread().getName() + "开始");
                if(container.getSize() < 5) {
                    try {
                        System.out.println(Thread.currentThread().getName() + "阻塞");
                        block.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println(Thread.currentThread().getName() + "结束");
                block.notify(); //唤醒线程1,继续执行
            }
        }, "线程2").start();

        new Thread(() -> {
            synchronized (block) {
                for(int i=0; i<10; i++) {
                    container.add("Object" + i);

                    if(container.getSize() == 5) {
                        block.notify(); //当唤醒在block上等待的线程后,线程1并不会失去锁,需要等线程1执行完毕释放锁,线程2才能接着执行。
                        try {
                            block.wait(); //唤醒线程2,释放掉线程1的锁
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }

                    try {
                        TimeUnit.SECONDS.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }, "线程1").start();

    }

}

看明白接着看,下面代码用到CountDownLatch,实现上面wait和notify的效果:

 1 import java.util.ArrayList;
 2 import java.util.List;
 3 import java.util.concurrent.CountDownLatch;
 4 import java.util.concurrent.TimeUnit;
 5 
 6 /**
 7  *
 8  * MyContainer4 改良版
 9  *
10  * 较MyContainer4 效率更高
11  *
12  */
13 public class MyContainer5 {
14 
15     volatile List<Object> list = new ArrayList<>();
16 
17     void add(Object o) {
18         try {
19             TimeUnit.SECONDS.sleep(1);
20         } catch (InterruptedException e) {
21             e.printStackTrace();
22         }
23         list.add(o);
24         System.out.println(Thread.currentThread().getName() + " add:" + o);
25     }
26 
27     int getSize() {
28         return this.list.size();
29     }
30 
31     public static void main(String[] args) {
32 
33         MyContainer5 container = new MyContainer5();
34 
35         //final Object block = new Object(); //
36         CountDownLatch latch = new CountDownLatch(5);
37 
38         new Thread(() -> {
39             //synchronized (block) {
40                 System.out.println(Thread.currentThread().getName() + "开始");
41                 if(container.getSize() < 5) {
42                     //try {
43                     System.out.println(Thread.currentThread().getName() + "阻塞");
44                     try {
45                         latch.await(); //等待门闩打开
46                     } catch (InterruptedException e) {
47                         e.printStackTrace();
48                     }
49                     //block.wait();
50                     //} catch (InterruptedException e) {
51                         //e.printStackTrace();
52                     //}
53                 }
54                 System.out.println(Thread.currentThread().getName() + "结束");
55                 //block.notify(); //唤醒线程1,继续执行
56             //}
57         }, "线程2").start();
58 
59         new Thread(() -> {
60             //synchronized (block) {
61                 for(int i=0; i<10; i++) {
62                     container.add("Object" + i);
63 
64                     //if(container.getSize() == 5) {
65                         //block.notify(); //当唤醒在block上等待的线程后,线程1并不会失去锁,需要等线程1执行完毕释放锁,线程2才能接着执行。
66                         //try {
67                             //block.wait(); //唤醒线程2,释放掉线程1的锁
68                         //} catch (InterruptedException e) {
69                             //e.printStackTrace();
70                         //}
71                     //}
72 
73                     latch.countDown(); //打开门闩,让线程2继续执行
74 
75                     try {
76                         TimeUnit.SECONDS.sleep(1);
77                     } catch (InterruptedException e) {
78                         e.printStackTrace();
79                     }
80                 }
81             //}
82         }, "线程1").start();
83 
84     }
85 
86 }

 

posted @ 2020-01-31 19:25  尘世间迷茫的小书童  阅读(250)  评论(0编辑  收藏  举报