java线程的wait和notify方法的说明


前言

我们线程的执行是抢占式的,线程的执行是我们不可预测的,但实际的开发中,我们希望合理的协调多个线程同时进行,但是要怎么办呢?于是我们就引入这个wait和notify的方法,接下来我们来介绍这俩个方法的应用场景和具体是怎么使用的.

一.wait方法

wait方法要做的事情:
使当前执行代码的线程进行等待. (把线程放到等待队列中)
释放当前的锁
满足一定条件时被唤醒, 重新尝试获取这个锁.
这样吧,我们来个例子来说明一下:

public class ThreadDemo20 {
    public static void main(String[] args) throws InterruptedException {
        Object object = new Object();
        System.out.println("wait 之前");
        synchronized (object) {
            object.wait();
        }
        System.out.println("wait 之后");
    }
}

当然这个程序运行起来之后,你会觉得很疑惑

在这里插入图片描述
实际上就是没有结束的,它相当于一直让线程进入了阻塞状态,当然我们线程必须有结束条件的.
结束条件:
其他线程调用该对象的 notify 方法.
wait 等待时间超时 (wait 方法提供一个带有 timeout 参数的版本, 来指定等待时间).
其他线程调用该等待线程的 interrupted 方法, 导致 wait 抛出 InterruptedException 异常.

当然我们先不对这个程序进行优化,先不使用notify方法,等我们介绍完notify方法是什么以后,然后对上述程序加一个notify方法.


二.notify方法

这里大概的列出它的方法概述:
方法notify()也要在同步方法或同步块中调用,该方法是用来通知那些可能等待该对象的对象锁的
其它线程,对其发出通知notify,并使它们重新获取该对象的对象锁。
如果有多个线程等待,则有线程调度器随机挑选出一个呈 wait 状态的线程。(并没有 “先来后到”)
在notify()方法后,当前线程不会马上释放该对象锁,要等到执行notify()方法的线程将程序执行
完,也就是退出同步代码块之后才会释放对象锁。

可能上面说了一大堆,你也没怎么明白,是怎么回事,我们来一个实际的生活例子,你就应该会明白了.

在这里插入图片描述
当然这个例子可能还是不好,但在后面我们会继续用一些实际性的例子,去让你慢慢明白的,不用着急.
接下来,要回到我们刚开始的那一个程序,我们要让wait结束,并唤醒,看完我接下来写的代码,或许你会有一定的思路.

public class ThreadDemo21 {
    public static void main(String[] args) throws InterruptedException {
        Object locker =new Object();
        Thread t1=new Thread(() ->{
            try {
            System.out.println("wait 开始");
            synchronized (locker){
                    locker.wait();
            }
                System.out.println("wait 结束");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        });
        t1.start();
        Thread.sleep(1000);
        Thread t2 = new Thread(() -> {
            synchronized (locker) {
                System.out.println("notify 开始");
                locker.notify();
                System.out.println("notify 结束");
            }
        });
        t2.start();
    }
}

在这里插入图片描述
当然执行结果如下图所示:
在这里插入图片描述

那如果我们的t1线程比t2线程后执行呢?那又会是什么结果呢?
大家看我用文字描述一番

如果线程t2先于线程t1运行,并调用了notify方法,但此时线程t1还没有调用wait方法,那么notify方法会没有任何效果,线程t1仍然会进入等待状态。输出结果如下:
wait 开始
notify 开始
notify 结束

代码更改就不展示了.因为只是线程的执行顺序不同,但执行效果如下:
在这里插入图片描述
从这里我们就可以发现,notify实际上是要和wait搭配使用的.如果没有陷入阻塞等待的线程,notify也不会报出异常,wait方法将一直等待下去,所以在这方面,我们还是要谨慎的.介绍完notify以后,我们还有一个notifyAll方法没有解释.

三.notifyAll方法

notify方法只是唤醒某一个等待线程. 使用notifyAll方法可以一次唤醒所有的等待线程.
我们接上文的例子,重新对原来的例子进行一些改进,看效果如下:

public class ThreadDemo22 {
    public static void main(String[] args) throws InterruptedException {
        Object locker =new Object();
        Thread t1=new Thread(() ->{
            try {
                System.out.println("wait 开始");
                synchronized (locker){
                    locker.wait();
                }
                System.out.println("wait 结束");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        });

        Thread t3=new Thread(() ->{
            try {
                System.out.println("wait 开始");
                synchronized (locker){
                    locker.wait();
                }
                System.out.println("wait 结束");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        });
        Thread t4=new Thread(() ->{
            try {
                System.out.println("wait 开始");
                synchronized (locker){
                    locker.wait();
                }
                System.out.println("wait 结束");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        });



        Thread t2 = new Thread(() -> {
            synchronized (locker) {
                System.out.println("notifyAll 开始");
                locker.notifyAll();
                System.out.println("notifyAll 结束");
            }
        });
        t1.start();
        t3.start();
        t4.start();
        Thread.sleep(1000);
        t2.start();


    }
}

执行结果:
在这里插入图片描述
此时可以看到, 调用 notifyAll 能同时唤醒 3 个wait 中的线程
当然特别注意,我们这里: 虽然是同时唤醒 3 个线程, 但是这 3 个线程需要竞争锁. 所以并不是同时执行, 而仍然是有先有后的执行.
比如那下面的图示举例子:
notify只唤醒等待队列中的一个线程. 其他线程还是乖乖等着
在这里插入图片描述
notifyAll 一下全都唤醒, 需要这些线程重新竞争锁
在这里插入图片描述

到这里大家先别急,我们先把这个篇文章结束,接下来的知识,我们在下一篇文章继续循序渐进

  • 5
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
### 回答1: Java中的waitnotify是多线程编程中的两个重要方法,用于线程之间的协作和通信。 wait方法可以使当前线程进入等待状态,直到其他线程调用notifynotifyAll方法唤醒它。在调用wait方法时,当前线程会释放它所持有的锁,以便其他线程可以访问共享资源。 notify方法用于唤醒一个处于等待状态的线程,如果有多个线程等待,则只会唤醒其中一个线程notifyAll方法则会唤醒所有处于等待状态的线程waitnotify方法必须在同步块中使用,即在使用这两个方法的对象上获取锁。否则会抛出IllegalMonitorStateException异常。 使用waitnotify方法可以实现线程之间的协作和通信,例如生产者消费者模型。在生产者消费者模型中,生产者线程生产据并将其放入共享队列中,消费者线程从队列中取出据并进行消费。当队列为空时,消费者线程需要等待生产者线程生产据,此时可以使用wait方法使消费者线程进入等待状态。当生产者线程生产据并将其放入队列中时,可以使用notify方法唤醒处于等待状态的消费者线程。 ### 回答2: Java线程中的 waitnotify 是两个非常重要的方法,它们可以帮助线程之间达成协作,实现复杂的操作。wait 方法用于让当前线程进入等待状态,直到其他线程通过 notify 方法通知它继续执行。notify 方法则用于唤醒一个等待状态的线程,使其继续执行。 wait 方法 wait 方法用于让当前线程进入等待状态,直到其他线程通过 notifynotifyAll 方法唤醒它。wait 方法需要在 synchronized 代码块中使用,否则会抛出 IllegalMonitorStateException 异常。在进入等待状态之后,线程将释放锁,并且进入一个等待池中,等待其他线程调用 notifynotifyAll 方法唤醒它。 notify 方法 notify 方法用于唤醒一个等待状态的线程,使其继续执行。notify 方法同样需要在 synchronized 代码块中使用,否则同样会抛出 IllegalMonitorStateException 异常。当一个线程调用 notify 方法时,等待池中的线程将会被唤醒,但是它们不能马上继续执行,必须等待当前线程释放锁。如果有多个线程等待池中,notify 方法只会唤醒其中一个线程,具体唤醒哪个线程是随机的。 notifyAll 方法 notifyAll 方法notify 方法类似,但是它会唤醒所有等待池中的线程notifyAll 方法同样需要在 synchronized 代码块中使用。 使用 waitnotify 实现线程协作 waitnotify 方法可以用来实现线程之间的协作,例如生产者和消费者问题。假设我们有一个共享的队列,生产者向队列中添加据,消费者从队列中取出据。如果队列已经满了,生产者就需要等待消费者取走据,如果队列是空的,消费者就需要等待生产者加入新据。 在这个问题中,我们可以使用 waitnotify 方法来实现线程之间的协作,代码如下: ``` public class Queue { private final List<Integer> items = new LinkedList<>(); private static final int MAX_SIZE = 10; public synchronized void produce(int item) throws InterruptedException { while (items.size() == MAX_SIZE) { wait(); } items.add(item); notify(); } public synchronized int consume() throws InterruptedException { while (items.isEmpty()) { wait(); } int item = items.remove(0); notify(); return item; } } public class Producer implements Runnable { private final Queue queue; public Producer(Queue queue) { this.queue = queue; } public void run() { for (int i = 0; i < 20; i++) { try { queue.produce(i); System.out.println("Produced: " + i); } catch (InterruptedException ex) { ex.printStackTrace(); } } } } public class Consumer implements Runnable { private final Queue queue; public Consumer(Queue queue) { this.queue = queue; } public void run() { for (int i = 0; i < 20; i++) { try { int item = queue.consume(); System.out.println("Consumed: " + item); } catch (InterruptedException ex) { ex.printStackTrace(); } } } } public class Main { public static void main(String[] args) throws InterruptedException { Queue queue = new Queue(); Thread producer = new Thread(new Producer(queue)); Thread consumer = new Thread(new Consumer(queue)); producer.start(); consumer.start(); producer.join(); consumer.join(); } } ``` 在这个示例代码中,我们创建了一个 Queue 类,它有两个方法 produce 和 consume 用于生产和消费据。在 produce 方法中,我们使用 while 循环来等待队列不满,如果队列已经满了,就调用 wait 方法进入等待状态。在 consume 方法中,我们使用 while 循环来等待队列不空,如果队列是空的,就调用 wait 方法进入等待状态。在生产新据或者消费据之后,我们都调用 notify 方法来唤醒等待池中的线程。 最后,我们可以使用 Producer 和 Consumer 类来生产和消费据,它们分别运行在不同的线程中。在运行这个程序时,生产者将不断生产据,消费者将不断消费据,一直到据生产完毕为止。在这个过程中,生产者和消费者之间通过 waitnotify 方法实现了线程之间的协作。 ### 回答3: Java是一种支持多线程的编程语言,在多线程编程过程中,一个线程可能需要等待另一个线程的某个条件满足后才能继续执行。Java提供了waitnotify来实现线程之间的协作。 wait:使当前线程进入等待状态,释放对象的锁,直到其他线程调用notifynotifyAll方法唤醒它。wait方法必须在持有对象锁的情况下调用,否则会抛出IllegalMonitorStateException异常。 notify:唤醒一个处于等待状态的线程,如果有多个线程等待,则只会唤醒其中一个,具体唤醒哪个线程无法预测notifyAll:唤醒所有处于等待状态的线程waitnotify必须在同步代码块中调用,并且针对同一个对象。waitnotify的调用顺序也非常重要,如果先调用了notify而没有等待线程,会导致唤醒失效。 在多线程编程中,waitnotify常常用于生产者和消费者模式中的线程之间的通信,生产者线程在生产完毕后调用notify方法唤醒消费者线程来消费据,消费者线程在消费完毕后调用wait方法等待下一个生产者线程的唤醒。 waitnotify的使用需要谨慎,如果使用不当,会导致死锁或线程饥饿等问题。同时,在Java SE 5之后,Java提供了更加高级的线程库,如ReentrantLock、Condition等,可以更加方便和安全地实现线程之间的协作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

忘忧记

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值