wait和notify方法

本文详细解读了Java中wait(), notify()和notifyAll()方法的用法,包括它们如何控制线程同步、唤醒机制以及与sleep的区别。通过实例演示,阐述了这些方法在多线程协作中的关键作用。
摘要由CSDN通过智能技术生成

目录

前言

一、wait()方法

wait()做的三件事

让wait()方法结束等待的条件

二、notify()方法

三、notifyAll()方法

总结

四、wait和sleep的对比【面试题】


前言

wait意思是等待,notify的意思是通知,它们的作用是协同多个线程的执行顺序

因为“抢占式执行”让两个线程的执行顺序充满了不确定性,所以我们希望通过wait和notify来控制线程的执行顺序,使线程之间可以更好的配合。

我们需要知道的是这两个方法都是Object(所以类的老祖宗)的方法

一、wait()方法

wait()做的三件事

1.让当前线程阻塞等待(把这个线程的PCB从就绪队列中放到阻塞队列中),准备接受通知

2.释放当前锁(要想使用wait()或者是notify(),就必须搭配synchronized,需要先获取到锁,然后才能谈wait())

3.需要满足一定的条件才能被唤醒,唤醒后重新尝试获取到这个锁

需要注意的是第一点和第二点需要以原子同时完成

让wait()方法结束等待的条件

1.其他线程调用该对象的notify()方法【最主要的用法】

我们已经知道了wait()和notify()都是Object的方法,就是说,如果线程1中的对象1调用了wait()方法,必须要有一个线程2,也调用对象1的notify()方法才能唤醒线程1,如果线程2调用对象2的notify()方法,是不能唤醒线程1的

public class ThreadDemo18 {
    public static void main(String[] args) throws InterruptedException {
        Object object=new Object();
        object.wait();
        object.notify();
    }
}

结果为

第一行的灰体带下滑线的意思是非法锁状态

如果没有把wait放到synchronized内部,就能看到这个异常

调用wait的对象和锁对象必须是同一个

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

    }
}

结果为

 从结果我们可以看出现在这个线程已经进入了阻塞队列中,我们也可以从jconsole.exe看出这个线程正在进行等待

 如果我们不对这个程序做什么的话这个线程就会永远等待下去,此时我们需要做点什么来使得这个线程等待状态结束,这里我们引入了notify方法

二、notify()方法

关于notify方法的使用

1.也需要放到synchronized中使用

2.notify操作是一次唤醒一个线程,如果是有多个线程都在等待中,调用notify就相当于是随机唤醒了一个,其它线程保持原状

3.调用notify是通知对方被唤醒,但是调用notify方法的线程并不是立刻释放锁,而是要等待当前的synchronized代码块执行完才释放锁(notify本身不会释放锁)

代码实例

public class ThreadDemo19 {
    static class WaitTask implements Runnable{
        private Object locker=null;

        public WaitTask(Object locker) {
            this.locker = locker;
        }

        @Override
        public void run() {
            //进行wait的线程
            synchronized (locker){
                System.out.println("wait开始前");
                try {
                    //直接调用wait,相当于this.wait(),也就是针对WaitTask的对象进行等待
                    //但是我们需要在下面在NotifyTask类中针对同一个对象进行通知
                    //然而,在NotifyTask中拿到WaitTask的对象并不容易
                    locker.wait();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println("wait结束");
            }
        }
    }
    static class NotifyTask implements Runnable{
        private Object locker=null;

        public NotifyTask(Object locker) {
            this.locker = locker;
        }

        @Override
        public void run() {
            //进行notify的线程
            synchronized (locker){
                System.out.println("notify开始前");
                locker.notify();
                System.out.println("notify结束");
            }
        }
    }
    public static void main(String[] args) throws InterruptedException {
        //所以为了解决上述问题,我们需要专门建一个对象,去负责加锁/通知操作
        Object locker=new Object();
        Thread t1=new Thread(new WaitTask(locker));
        Thread t3=new Thread(new WaitTask(locker));
        Thread t4=new Thread(new WaitTask(locker));
        Thread t2=new Thread(new NotifyTask(locker));
        t1.start();
        t3.start();
        t4.start();
        Thread.sleep(3000);
        t2.start();
    }
}

运行结果为

三、notifyAll()方法

notify方法一次只能唤醒一个线程,而notifyAll可以唤醒多个,虽然多个线程都被唤醒了,但这几个线程执行还是有先后顺序的原因是这几个线程还要竞争锁

public void run() {
            //进行notify的线程
            synchronized (locker){
                System.out.println("notify开始前");
                locker.notifyAll();
                System.out.println("notify结束");
            }
        }

运行结果为

2.wait()等待时间超时

3.其它线程调用该等待线程的interrupted()方法,导致wait抛出InterruptedExpectation异常

总结

notify是一次唤醒一个,而notifyAll是一次唤醒一堆,具体用哪个还要看具体场景,一般来说用的是notify

四、wait和sleep的对比【面试题】

1.sleep操作是指定一个固定时间来阻塞等待,而wait既可以指定时间,也可以无限等待

2.wait可以通过notify或interrupt或时间到来唤醒,sleep通过interrupt或时间到唤醒

3.wait的主要用途是为了协调线程之间的先后顺序,而sleep单纯是让线程休眠,并没涉及到多个线程的配合

### 回答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等,可以更加方便和安全地实现线程之间的协作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值