zoukankan      html  css  js  c++  java
  • 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

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

    (完)

  • 相关阅读:
    DataTable 只保留想要的几列
    如何用多个字符串来切分字符串
    用.net 发送邮件
    sqlserver 行列转换
    sql面试题一 学生成绩
    将DataReader转换为DataTable
    C# 如何用多个字符串来切分字符串并去除空格
    SqlServer按时间自动生成生成单据编号
    实验一 Java开发环境的熟悉
    Java学习笔记心得——初识Java
  • 原文地址:https://www.cnblogs.com/QG-whz/p/8336724.html
Copyright © 2011-2022 走看看