zoukankan      html  css  js  c++  java
  • Java线程之wait()、notify()、notifyAll()

    翻译:https://www.journaldev.com/1037/java-thread-wait-notify-and-notifyall-example

    简述

    java中Objct对象包含三个方法用于线程之间对于资源锁状态进行通讯,这三个方法分别是wait()、notify()、notifyAll(),今天我们就来学习一下这三个方法的应用。
    任何对象上调用这些方法的当前线程都应该具有对象监视器,否则它将抛出java.lang.IllegalMonitorStateException

    方法介绍

    wait

    wait()方法会让当前线程处于等待状态,同时也会释放当前线程所持有的锁,直到其它线程调用了该对象的notify()或notifyAll()方法,该对象才会被唤醒进入就绪状态,wati有三个重载方法,分别是:

    #一直等待,直到被唤醒
    public final void wait() throws InterruptedException
    #一直等待,直到被唤醒或超超时(毫秒)
    public final native void wait(long timeout) throws InterruptedException;
    #一直等待,直到被唤醒或超超时(纳秒)
    public final void wait(long timeout, int nanos) throws InterruptedException

    notify

    唤醒等待当前对象的线程,如果有多个对象等待,将唤醒其中的一个

    notifyAll

    唤醒等待当前对象的所有线程

    代码实例

    这些方法可用于实现生产者-消费者问题,其中消费者线程正在等待队列中的对象,生产者线程将对象放入队列,并通知等待的线程

    让我们来看一个例子,其中多个线程在同一个对象上工作,我们使用wait、notify和notifyAll方法

    消息对象:

    package com.lkf.mulithread;
    
    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;
        }
    }
    

    消费者线程

    package com.lkf.mulithread;
    
    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+" 正在等待唤醒:"+System.currentTimeMillis());
                    msg.wait();
                }catch(InterruptedException e){
                    e.printStackTrace();
                }
                System.out.println(name+" 已被唤醒:"+System.currentTimeMillis());
                //process the message now
                System.out.println(name+" 消息处理完成: "+msg.getMsg());
            }
        }
    
    }
    

    生产者线程

    package com.lkf.mulithread;
    
    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+" 已经启动");
            try {
                Thread.sleep(1000);
                synchronized (msg) {
                    msg.setMsg(name+" 我是一条消息");
                    msg.notify();
                    // msg.notifyAll();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    
        }
    
    }
    package com.lkf.mulithread;
    
    public class WaitNotifyTest {
        public static void main(String[] args) {
            Message msg = new Message("process it");
            Waiter waiter1 = new Waiter(msg);
            new Thread(waiter1, "1号线程").start();
    
            Waiter waiter2 = new Waiter(msg);
            new Thread(waiter2, "2号线程").start();
    
            Notifier notifier = new Notifier(msg);
            new Thread(notifier, "唤醒线程").start();
            System.out.println("所有线程启动完毕");
        }
    }
    

    当我们执行测试程序,会输出以下结果,但是程序执行并没有完成,因为我们调用了notify()方法,只是唤醒了其中的一个线程,另外一个线程在一直等待

    1号线程 正在等待唤醒:1521883402227
    2号线程 正在等待唤醒:1521883402228
    所有线程启动完毕
    唤醒线程 已经启动
    1号线程 已被唤醒:1521883403230
    1号线程 消息处理完成: 唤醒线程 我是一条消息

    当我们调用notifyAll()方法时,会唤醒所有等待该对象的线程,会输出以下结果:

    1号线程 正在等待唤醒:1521883786348
    2号线程 正在等待唤醒:1521883786348
    所有线程启动完毕
    唤醒线程 已经启动
    2号线程 已被唤醒:1521883787353
    2号线程 消息处理完成: 唤醒线程 我是一条消息
    1号线程 已被唤醒:1521883787353
    1号线程 消息处理完成: 唤醒线程 我是一条消息

    总结一下:

    1.一定要在synchronized中使用wait()/notify()/notifyAll(),先获取对象锁,否则jvm会抛出IllegalMonitorStateException异常。

    2.使用wait()时,判断线程是否进入wait状态的条件一定要使用while而不要使用if,因为等待线程可能会被错误地唤醒,所以应该使用while循环在等待前等待后都检查唤醒条件是否被满足,保证安全性。

    3.notify()或notifyAll()方法调用后,线程不会立即释放锁,只会将wait中的线程从等待队列移到同步队列,也就是线程状态从waitting变为blocked;

    4.wait()方法返回的前提是线程重新获得了对象的锁

  • 相关阅读:
    缺席多年的东哥,重回博客了
    使用VMware安装CentOS 7
    Linux CentOS 7 搭建 Tomcat 8 服务器
    海思HI3518EV200+AR0130开发板DIY——前篇
    Keyshot+AD渲染PCB效果图
    ESP8266/ESP32模块晶振频偏调试
    关于摄像头PCB图设计经验谈
    docker容器虚拟化技术
    数据分析章节(一):初始数学之美
    Nginx:反向代理
  • 原文地址:https://www.cnblogs.com/liukaifeng/p/10052662.html
Copyright © 2011-2022 走看看