Java多线程之wait和notify的使用

目录

前言

一、举个例子

二、wait()和notify()

2.1wait()的介绍

2.2wait()和notify()的基本使用

2.3wait()的带参数版本

2.4 notify()和notifyAll()

三、wait和sleep的对比


前言

        在前面我们已经介绍过了join()方法可以用来控制线程的执行顺序了,但是join()却只能让整个线程执行结束再去执行另外的线程。控制线程执行顺序的效果是有限的,因此又有了wait()和notify(),目的其实都是为了控制程序的执行顺序的。本篇博客就主要来介绍wait()和notify()的使用。

一、举个例子

        有一天,A B C三个人去银行的ATM取钱,此时ABC可以理解为三个线程,A进去之后,就把门锁了,接着发现ATM里没钱了,就出来了,就相当于A释放了锁,接着A B C三人又开始竞争锁了,这时候谁抢到锁,那就是随机的了。这时候就可能就会出现极端情况:A运气好,一直都抢到锁,一直在ATM中进进出出,B C线程就一直没被执行到,此时B C就称为线程饿死,这样效率就变低了。此时我们使用wait()就可以解决这个问题了,具体来说就是:A进去之后,发现ATM里没钱了,就wait(),也就是阻塞等待了,这时候就释放锁了,在阻塞等待过程中就不会和B C竞争锁了。这时候BC就有机会拿到锁了,就可以执行了。A阻塞等待一段时间之后,这时候可能就有工作人员去把钱存到ATM里面了或者有人往ATM里存钱了,这时候别的人就告诉A说可以进去取钱了,(也就是当其他线程发现A的条件满足时,就调用notify去唤醒A,这就是notify的用法)。这就是wait和notify的大概使用场景。

二、wait()和notify()

2.1wait()的介绍

wait()方法其实主要做三件事:

①把当前线程放进等待队列中

②释放锁

③被其他线程唤醒时,尝试重新获取锁

接下来看一段问题代码: 

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

运行结果: 

        代码分析:我们可以看到,这个代码只执行到了打印wait()之前,接着就抛出了一个异常,这个异常是非法的锁状态异常,这是因为,因为这时候我们压根没有对wait()这个方法加锁,就尝试着要去解锁(wait()方法会释放锁),这完全是不符合逻辑的,因此就抛出了异常。所以wait()是需要搭配synchronizaed使用的。

2.2wait()和notify()的基本使用

    public static void main(String[] args) throws InterruptedException {
       Object locker = new Object();//创建一个锁对象
       Thread t1 = new Thread(()->{
           System.out.println("wait()之前");
           synchronized (locker){//先进行加锁操作
               try {
                   locker.wait();
               } catch (InterruptedException e) {
                   throw new RuntimeException(e);
               }
           }
           System.out.println("wait()之后");
       });
       t1.start();
       Thread.sleep(1000);//等待1s确保t1已经执行到wait
       Thread t2 = new Thread(()->{
           System.out.println("notify()之前");
           synchronized (locker){
               locker.notify();//唤醒线程t1
           }
           System.out.println("notify()之后");
       });
       t2.start();
    }

        运行结果:我们在这里先创建了一个锁对象,这里执行过程是这样的wait()执行之后,t1进入阻塞等待,并释放了锁,接着就开始执行t2,t2开始执行之后就获取了锁,接着调用了notify()唤醒了t1线程,并且释放了锁,然后t1被唤醒了之后就继续往下执行了。

       注意事项:在上面代码中,在wait时候线程会进入WAITING状态,wait、synchronized对象必须是同一个才能正确的加锁,wait和notify的对象也必须是同一个,才可以正确唤醒。而且必须保证wait是在notify之前,如果notify执行在wait之前,程序不会报错,也不会抛异常,相当于是一次无效操作。

2.3wait()的带参数版本

wait() 无参数版本

wait(long) 指定最大等待时间,单位毫秒

wait(long,int) 精度更高,前面是毫秒,后面是纳秒

我们上面的例子wait()是一个不带参数的版本,也就意味着这里的wait()是一个死等的状态,但在实际使用的时候,我们是很少会让线程死等的。我们一般使用wait()的有参数版本,指定最大等待时间。

2.4 notify()和notifyAll()

        notify()和notifyAll()这两个方法通过方法名就很容易可以看出来区别,前者是唤醒一个线程,后者是唤醒全部的线程。

notifyAll的一个例子:

        开头我们讲了三个人A B C三个人同时去取钱,当三个人都进去ATM里面看完之后,发现ATM(三个线程wait了)里面没钱,就都会进入阻塞等待状态,而当工作人员将钱放入ATM里面之后,这时候工作人员去通知这三个人来取钱了(此时就相当于调用notifyAll方法了,这里唤醒之后三个线程是会竞争锁的而不是同时执行的)。

注意事项:

        一般情况下,我们都是使用notify,因为notifyAll唤醒全部,要慎重使用。

三、wait和sleep的对比

        wait和sleep其实是没有可比性的,因为一个是用于线程之间的通信的,一个是让线程阻塞一段时间。这两个方法设计的初心是不同的,一个是单纯为了让线程进行阻塞等待而休眠一段时间,而一个是为了解决线程的执行顺序的问题。最明显的区别就是wait需要搭配锁使用,而sleep不需要。wait 是 Object 的方法 sleep 是 Thread 的静态方法。他们唯一的相同点就是都可以让线程进入阻塞等待状态。

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答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、付费专栏及课程。

余额充值