Java Thread wait、notify与notifyAll

Java的Object类包含了三个final方法,允许线程就资源的锁定状态进行通信。这三个方法分别是:wait(),notify(),notifyAll(),今天来了解一下这三个方法。在任何对象上调用这些方法的当前线程应具有对象监视器(锁住了一个对象,就是获得对象相关联的监视器),否则会抛出java.lang.IllegalMonitorStateException异常。

wait

Object.wait有三种重载的实现,一个无限期等待任何其他线程地调用对象的notify或notifyAll方法来唤醒当前线程。 其他两个会使当前线程在等待特定的时间后进行唤醒。

wait()

使得当前线程进程等待,直到另一个线程在这个对象上调用了notify()方法或者notifyAll()方法。这个方法的行为,完全等价于调用wait(0),可以看它的实现代码为:

    public final void wait() throws InterruptedException {
        wait(0);
    }

当前的线程必须获得这个对象的监视器。线程释放了监视器的所有权,直到另一个线程调用notify方法或者notifyAll方法,去唤醒在这个对象监视器上等待的其他线程。释放了监视器的所有权后,线程便进行等待,直到重新获得监视器的所有权并恢复执行。处于wait状态中的线程,可能被中断或虚假唤醒,所以这个方法应该总是在一个循环中使用:

         synchronized (obj) {
              while (<condition does not hold>)
                  obj.wait();
              ... // Perform action appropriate to condition
          }

虚假唤醒指的是一些obj.wait()会在除了obj.notify()和obj.notifyAll()的其他情况被唤醒,而此时是不应该唤醒的,更详细的可以看Spurious_wakeup

方法会抛出两种异常:

IllegalMonitorStateException:如果当前线程没有获得当前对象的监视器。

InterruptedException:如果某个线程在当前线程等待通知的时候,或是在等待通知之前中断了当前线程,当抛出这个异常时,当前线程的中断状态被清除。

wait(long timeout)

该方法会使得当前进程进入wait状态直到另一个线程调用notify/notifyAll,或是经过了指定的时间,线程将被唤醒。该方法会抛出异常:

IllegalArgumentException:如果timeout是负数。

wait(long timeout, int nanos)

同样的,该方法会使得当前进程进入wait状态直到另一个线程调用notify/notifyAll。它可以更加精细地控制等待的时间,以纳秒为单位测量的实时量由下式给出:1000000*timeout+nanos。除此之外,这个方法与wait(long)做相同的事情,特别的,wait(0,0)等价于wait(0)。除了其余两个wait方法会抛出的异常外,这个方法会抛出异常:

IllegalArgumentException:如果timeout是负数,或者nanos的范围不在0-999999之间时,抛出该异常。

notify

notify方法只唤醒等待对象的一个线程,并且该线程开始执行。所以如果有多个线程在等待一个对象,这个方法只会唤醒其中的一个。线程的选择取决于线程管理的OS实现。

notifyAll

notifyAll方法唤醒等待对象的所有线程,但哪一个将首先处理取决于操作系统的实现。

这些方法可用于实现生产者消费者问题,其中消费者线程正在等待队列中的对象,生产者线程将对象放入队列中并通知等待的线程。下面是一个多个线程工作在同一个对象上的例子,使用了wait,notify,notifyAll方法:

 一个例子

Message :被通知唤醒的对象

public class Message {
    private String msg;

    public Message(String str){
        this.msg=str;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String str) {
        this.msg=str;
    }

}

Notifier:执行唤醒操作

public class Notifier implements Runnable {

    private Message msg;

    public Notifier(Message msg) {
        this.msg = msg;
    }

    @Override
    public void run() {
        String name = Thread.currentThread().getName();
        System.out.println(name+" started");
        try {
            Thread.sleep(1000);
            synchronized (msg) {
                msg.setMsg(name+" Notifier work done");
                msg.notify();
//                 msg.notifyAll();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

}

Waiter: 使Message 对象进行wait状态

public class Waiter implements Runnable{

    private Message msg;

    public Waiter(Message m){
        this.msg=m;
    }

    @Override
    public void run() {
        String name = Thread.currentThread().getName();
        synchronized (msg) {
            try{
                System.out.println(name+" waiting to get notified at time:"+System.currentTimeMillis());
                msg.wait();
            }catch(InterruptedException e){
                e.printStackTrace();
            }
            System.out.println(name+" waiter thread got notified at time:"+System.currentTimeMillis());
            //process the message now
            System.out.println(name+" processed: "+msg.getMsg());
        }
    }

}

WaitNotifyTest:测试

public class WaitNotifyTest {

    public static void main(String[] args) {
        Message msg = new Message("process it");
        Waiter waiter = new Waiter(msg);
        new Thread(waiter,"waiter").start();

        Waiter waiter1 = new Waiter(msg);
        new Thread(waiter1, "waiter1").start();

        Notifier notifier = new Notifier(msg);
        new Thread(notifier, "notifier").start();
        System.out.println("All the threads are started");
    }

}

输出:

waiter waiting to get notified at time:1516757290631
All the threads are started
waiter1 waiting to get notified at time:1516757290632
notifier started
waiter waiter thread got notified at time:1516757291632
waiter processed: notifier Notifier work done

可以看到两个线程在对象msg上进行等待,调用notify方法时,只有一个线程被唤醒,此时程序并没有退出,因为还有一个线程在等待。

如果把notify方法改成notifyAll,运行结果为:

waiter waiting to get notified at time:1516757437164
waiter1 waiting to get notified at time:1516757437164
All the threads are started
notifier started
waiter1 waiter thread got notified at time:1516757438165
waiter1 processed: notifier Notifier work done
waiter waiter thread got notified at time:1516757438165
waiter processed: notifier Notifier work done

可以看到两个线程都被唤醒了,程序也退出运行了。

(完)

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏mathor

线程的基本概念

1074
来自专栏JetpropelledSnake

Python学习笔记之Python对象反射、类反射、模块反射

1384
来自专栏mwangblog

bash中的变量

1228
来自专栏娱乐心理测试

vue 方法回调通知执行下一个方法

在项目中有很多这样的需求,在一个方法执行完成拿到数据后才可以执行下一个方法,这就需要在第一个方法执行完后有个回调函数通知下一个方法可以执行了。

622
来自专栏Python小屋

列表元素循环移位中Python切片的妙用

之前有个文章中介绍了列表循环移位的3中方法,原文请见:Python序列循环移位的3种方法 其中第二种方法虽然更直接地翻译了题目的要求,但是显得还是有点啰嗦,如...

2734
来自专栏猿人谷

unix共享内存要点

共享内存优点:     1.在进程之间不通过内核传递数据,即不通过系统调用拷贝数据,达到快速,高效的数据传输。     2.随内核持续     *nix的共享内...

17410
来自专栏Web项目聚集地

你真的懂「类的加载机制」吗?

高广超 :多年一线互联网研发与架构设计经验,擅长设计与落地高可用、高性能互联网架构。目前就职于美团网,负责核心业务研发工作。本文首发在 高广超的简书博客,欢迎点...

1163
来自专栏Janti

Java多线程高并发学习笔记(一)——Thread&Runnable

 进程与线程 首先来看百度百科关于进程的介绍: 进程是一个具有独立功能的程序关于某个数据集合的一次运行活动。它可以申请和拥有系统资源,是一个动态的概念,是一个活...

59110
来自专栏黑白安全

C++如何调用class类中方法实现多线程编程

众所周知在使用C++创建多线程执行时只能传递一个方法到thread模块中去创建线程执行。但是有时候我们往往需要使用多线程去执行某个对象中的方法,而对象中的方法却...

1082
来自专栏JackeyGao的博客

Django小技巧08: Blank or Null

Django Model API 中提供了blank和null两个参数, 非常容易混淆。当我第一次使用 Django 的时候, 总是不能恰当的使用这两个参数。

583

扫码关注云+社区