java多线程学习4:wait、notify、notifyall。生产者消费者实例

大纲:
1wait
2、notify
3、notifyall
4waitsleep的区别
5、单一生产者消费者代码
6、多生产者消费者代码

wait
官方文档太多。总结为下面几点
1、调用wait方法必须要有monitor。这个monitor就是synchorized中的那个。
2、调用wait后,当前线程会进去wait set(线程休息室)并释放当前锁占用。直到thismonitor的notify、notifyall、或者wait超时才能唤醒。
3、当发生当前上述情况后,线程会从线程wait set中出来,并重新变为可调度(runnable),通常会争抢这个锁。当它获取到执行权时,就会恢复到之前的执行状态。

notify
还是总结一下官方文档说的
1、这个方法用来唤醒是用同一mointor的wait中的线程。
2、当前线程调用monitor的notify后,线程并不会马上释放锁。需要当前线程放弃锁占用。
3、调用Notify需要首先获取锁占用
4、当wait的线程有多个时,notify的唤醒时随机的(可能根据不同jvm有不同的策略)。

notifyall
这个就真的没什么好说的了。唤醒所有wait住的线程(同1monitor)

wait和sleep的区别
1、wait是object的实例方法,sleep是thread的静态方法。
2、wait和sleep都会block住当前线程。但wait会释放锁占用而sleep不会释放锁,只是然出cpu的执行权。
3、wait的使用必须要在同步代码块中,同时必须时当前monitor调用。sleep没有这个限制

单生产者消费者实例

package com.puqiuyu.thread.blog;

public class Window {
    private int ticket;

    private final Object MONITOR;

    private boolean isProduced;

    public Window() {
        this.MONITOR = new Object();
        this.isProduced = false;
    }

    public void produce() {
        synchronized (MONITOR) {
            while (isProduced) {
                try {
                    MONITOR.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            ticket++;
            System.out.println("produce ticket : " + ticket);
            isProduced = true;
            MONITOR.notify();
        }
    }
    public void consume() {
        synchronized (MONITOR) {
            while(!isProduced) {
                try {
                    MONITOR.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("consume ticket : " + ticket);
            isProduced =false;
            MONITOR.notify();
        }
    }

}
package com.puqiuyu.thread.blog;

public class ProducerAndConusmerDemo {
    public static void main(String[] args) {

        final int count = 1000;
        final Window window = new Window();
        Thread producer = new Thread(()->{
            for (int i = 0; i < count; i++) {
                window.produce();
            }
        });
        Thread consume = new Thread(()->{
            for (int i = 0; i < count; i++) {
                window.consume();
            }
        });
        producer.start();
        consume.start();
    }
}

上面这段程序,执行不会存在问题。但如果改成多生产者消费者就会出现问题。

现在起了2个生产者、两个消费者。结果线程假死
通过jstack来分析一下

"consumer_1" #12 prio=5 os_prio=31 tid=0x00007fd23c016000 nid=0x5303 in Object.wait() [0x000070000fad5000]
   java.lang.Thread.State: WAITING (on object monitor)
    at java.lang.Object.wait(Native Method)
    - waiting on <0x00000007955f57b8> (a java.lang.Object)
    at java.lang.Object.wait(Object.java:502)
    at com.puqiuyu.thread.blog.Window.consume(Window.java:34)
    - locked <0x00000007955f57b8> (a java.lang.Object)
    at com.puqiuyu.thread.blog.ProducerAndConusmerDemo.lambda$3(ProducerAndConusmerDemo.java:25)
    at com.puqiuyu.thread.blog.ProducerAndConusmerDemo$$Lambda$4/142257191.run(Unknown Source)
    at java.lang.Thread.run(Thread.java:748)

"consumer_0" #11 prio=5 os_prio=31 tid=0x00007fd23c015000 nid=0x5103 in Object.wait() [0x000070000f9d2000]
   java.lang.Thread.State: WAITING (on object monitor)
    at java.lang.Object.wait(Native Method)
    - waiting on <0x00000007955f57b8> (a java.lang.Object)
    at java.lang.Object.wait(Object.java:502)
    at com.puqiuyu.thread.blog.Window.consume(Window.java:34)
    - locked <0x00000007955f57b8> (a java.lang.Object)
    at com.puqiuyu.thread.blog.ProducerAndConusmerDemo.lambda$2(ProducerAndConusmerDemo.java:20)
    at com.puqiuyu.thread.blog.ProducerAndConusmerDemo$$Lambda$3/135721597.run(Unknown Source)
    at java.lang.Thread.run(Thread.java:748)

"producer_1" #10 prio=5 os_prio=31 tid=0x00007fd23b034000 nid=0x4f03 in Object.wait() [0x000070000f8cf000]
   java.lang.Thread.State: WAITING (on object monitor)
    at java.lang.Object.wait(Native Method)
    - waiting on <0x00000007955f57b8> (a java.lang.Object)
    at java.lang.Object.wait(Object.java:502)
    at com.puqiuyu.thread.blog.Window.produce(Window.java:19)
    - locked <0x00000007955f57b8> (a java.lang.Object)
    at com.puqiuyu.thread.blog.ProducerAndConusmerDemo.lambda$1(ProducerAndConusmerDemo.java:15)
    at com.puqiuyu.thread.blog.ProducerAndConusmerDemo$$Lambda$2/303563356.run(Unknown Source)
    at java.lang.Thread.run(Thread.java:748)

"producer_0" #9 prio=5 os_prio=31 tid=0x00007fd23a8bf800 nid=0x4d03 in Object.wait() [0x000070000f7cc000]
   java.lang.Thread.State: WAITING (on object monitor)
    at java.lang.Object.wait(Native Method)
    - waiting on <0x00000007955f57b8> (a java.lang.Object)
    at java.lang.Object.wait(Object.java:502)
    at com.puqiuyu.thread.blog.Window.produce(Window.java:19)
    - locked <0x00000007955f57b8> (a java.lang.Object)
    at com.puqiuyu.thread.blog.ProducerAndConusmerDemo.lambda$0(ProducerAndConusmerDemo.java:10)
    at com.puqiuyu.thread.blog.ProducerAndConusmerDemo$$Lambda$1/531885035.run(Unknown Source)
    at java.lang.Thread.run(Thread.java:748)
可以看到 p0\p1\c0\c1都在wait这个<0x00000007955f57b8>monitor。
什么情况导致呢,因为notify只唤醒一个,且有随机性。那么当一个producer wait之后刚好唤醒了另一个producer。而此时已经生产了1个。rpdoucer有wait了。那么就会出现这种问题。
所以我们将notify改为notifyall解决这个问题。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值