wait、notify和notifyAll的使用

首先,wait、notify和notifyAll  这三个 都是Object类里的方法,可以用来控制线程的状态

解释:

如果对象调用了wait方法就会使持有该对象的线程把该对象的控制权交出去,然后处于等待状态。

如果对象调用了notify方法就会通知某个正在等待这个对象的控制权的线程可以继续运行。

如果对象调用了notifyAll方法就会通知所有等待这个对象控制权的线程继续运行。

下来有个生产者消费者的列子, 提供理解:

package com.luxins.join.join;

import java.util.Queue;

public class Consumer implements Runnable {
    //背包
    Queue<String> bages;
    //大小
    int size;
    public Consumer(Queue<String> bages, int size) {
        this.bages = bages;
        this.size = size;
    }

    @Override
    public void run() {
        while (true) {
            synchronized (bages) {
                while (bages.isEmpty()) {
                    try {
                        System.out.println("背包满了了");
                        bages.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                //睡眠1s
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                String bage = bages.remove();
                System.out.println("当前消费者的数量为:" + bage);

                //唤醒生产者(因为是双向的,又是同一个对象bages下)
                bages.notifyAll();


            }
        }

    }
}

 

package com.luxins.join.join;

import java.util.Queue;

public class producer implements Runnable {
    Queue<String> bages;
    int size;

    public producer(Queue<String> bages, int size) {
        this.bages = bages;
        this.size = size;
    }

    @Override
    public void run() {
        int i = 0;
        while (true) {
            i++;
            synchronized (bages) {
                while (bages.size() == size) {
                    System.out.println("背包已经装满了");
                    try {
                        bages.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                System.out.println("当前生产者数量为:"+i);
                bages.add("bages"+i);
                //唤醒消费者
                bages.notifyAll();

            }

        }


    }
}
package com.luxins.join.join;

import java.util.LinkedList;
import java.util.Queue;

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

        Queue<String> a = new LinkedList<>();
        int size = 10;
        Consumer consumer = new Consumer(a, size);
        producer producer = new producer(a, size);
        Thread t1 = new Thread(consumer);
        Thread t2 = new Thread(producer);
        t2.start();
        t1.start();



    }
}

 

 


做多 10条,可能在没满 10之前 ,就已经 用了
情况是随机的
还有就是不一定就是生产一条 一定会消费一条

有可能生产了两条 只消费一条 然后继续生产

cup  不同,在不同电脑上出现的 效果 就不一样

这个例子就是

一个线程修改了一个对象的值,而另个线程感
知到了变化,然后进行响应的操作,是双向的,并且同一个对象的基础之上(bage)

1. 从刚刚的例子来看,其实wait/notify本质上其实是一种条件的竞争,至少来说,
wait和notify方法一定是互斥存在的,既然要实现互斥,那么synchronized就是一
个很好的解决方法
2. wait和notify是用于实现多个线程之间的通信,而通信必然会存在一个通信的
载体,比如我们小时候玩的游戏,用两个纸杯,中间用一根线连接,然后可以实现
比较远距离的对话。而这根线就是载体,那么在这里也是一样,wait/notify是基
于synchronized来实现通信的。也就是两者必须要在同一个频道也就是同一个锁的
范围内
 
最后,
其实说了这么多,一句话解释就是之所以我们应该尽量使用notifyAll()的原因就是,notify()非常容易导致死锁
当然notifyAll并不一定都是优点,毕竟一次性将Wait Set中的线程都唤醒是一笔不菲的开销,如果你能handle你的线程调度,那么使用notify()也是有好处的。

我们在调用wait()方法的时候,心里想的肯定是因为当前方法不满足我们指定的条件,因此执行这个方法的线程需要等待直到其他线程改变了这个条件并且做出了通知。

那么为什么要把wait()方法放在循环而不是if判断里呢,其实答案显而易见,因为wait()的线程永远不能确定其他线程会在什么状态下notify(),

所以必须在被唤醒、抢占到锁并且从wait()方法退出的时候再次进行指定条件的判断,以决定是满足条件往下执行呢还是不满足条件再次wait()呢。

就像在本例中,如果只有一个生产者线程,一个消费者线程,那其实是可以用if代替while的,因为线程调度的行为是开发者可以预测的,

生产者线程只有可能被消费者线程唤醒,反之亦然,因此被唤醒时条件始终满足,程序不会出错。

但是这种情况只是多线程情况下极为简单的一种,更普遍的是多个线程生产,多个线程消费,

那么就极有可能出现唤醒生产者的是另一个生产者或者唤醒消费者的是另一个消费者,这样的情况下用if就必然会现类似过度生产或者过度消费的情况了,

典型如IndexOutOfBoundsException的异常。所以所有的java书籍都会建议开发者永远都要把wait()放到循环语句里面




posted on 2020-04-27 23:06  小白菜好吃  阅读(1193)  评论(0编辑  收藏  举报

导航