博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java多线程 wait, notify 和 notifyAll
阅读量:6974 次
发布时间:2019-06-27

本文共 10676 字,大约阅读时间需要 35 分钟。

Java的Object类

public class Object {        public final native void notify();        public final native void notifyAll();        public final native void wait(long timeout) throws InterruptedException;    }

调用这些方法的当前线程必须拥有此对象监视器,否则将会报java.lang.IllegalMonitorStateException exception

wait;

Object的wait方法有三个重载方法,其中一个方法wait() 是无限期(一直)等待,直到其它线程调用notify或notifyAll方法唤醒当前的线程;另外两个方法wait(long timeout) 和wait(long timeout, int nanos)允许传入 当前线程在被唤醒之前需要等待的时间,timeout为毫秒数,nanos为纳秒数。

wait():释放占有的对象锁,线程进入等待池,释放cpu,而其他正在等待的线程即可抢占此锁,获得锁的线程即可运行程序。

而sleep()不同的是,线程调用此方法后,会休眠一段时间,休眠期间,会暂时释放cpu,但并不释放对象锁。也就是说,在休眠期间,其他线程依然无法进入此代码内部。休眠结束,线程重新获得cpu,执行代码。

wait()和sleep()最大的不同在于wait()会释放对象锁,而sleep()不会!

notify:

notify方法只唤醒一个等待(对象的)线程并使该线程开始执行。

调用notify()后,并不会立即释放锁,而是继续执行当前代码,直到synchronized中的代码全部执行完毕,才会释放对象锁。JVM则会在等待的线程中调度一个线程去获得对象锁,执行代码。

需要注意的是,wait()和notify()必须在synchronized代码块中调用

所以如果有多个线程等待一个对象,这个方法只会唤醒其中一个线程,选择哪个线程取决于操作系统对多线程管理的实现。

notifyAll:

notifyAll 会唤醒所有等待(对象的)线程,尽管哪一个线程将会第一个处理取决于操作系统的实现。

这些方法可以使用于“生产者-消费者”问题,消费者是在队列中等待对象的线程,生产者是在队列中释放对象并通知其他线程的线程。

demo:

public class Main {    public static void main(String[] args) {        Message msg = new Message("process it");        Waiter waiter = new Waiter(msg);        new Thread(waiter,"waiterThread").start();        Waiter waiter1 = new Waiter(msg);        new Thread(waiter1, "waiter1Thread").start();        Notifier notifier = new Notifier(msg);        new Thread(notifier, "notifierThread").start();        System.out.println("All the threads are started");    }}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;    }}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());        }    }}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();        }    }}
All the threads are startedwaiter1Thread waiting to get notified at time:1480592377675waiterThread waiting to get notified at time:1480592377677notifierThread startedwaiter1Thread waiter thread got notified at time:1480592378685waiter1Thread processed: notifierThread Notifier work done
View Code

Notifier中的run方法改成msg.notifyAll();

Result:

All the threads are startedwaiter1Thread waiting to get notified at time:1480592533780waiterThread waiting to get notified at time:1480592533780notifierThread startedwaiterThread waiter thread got notified at time:1480592534782waiterThread processed: notifierThread Notifier work donewaiter1Thread waiter thread got notified at time:1480592534782waiter1Thread processed: notifierThread Notifier work done
View Code

一个通知线程,3个等待线程:

public class Main {    private String flag[] = { "true" };    public static void main(String[] args) {        System.out.println("Main Thread Run!");        Main test = new Main();        System.nanoTime();        NotifyThread notifyThread =test.new NotifyThread("notify01");        WaitThread waitThread01 = test.new WaitThread("waiter01");        WaitThread waitThread02 = test.new WaitThread("waiter02");        WaitThread waitThread03 = test.new WaitThread("waiter03");        notifyThread.start();        waitThread01.start();        waitThread02.start();        waitThread03.start();    }    class NotifyThread extends Thread{        public NotifyThread(String name) {            super(name);        }        public void run() {            try {                sleep(3000);//推迟3秒钟通知            } catch (InterruptedException e) {                e.printStackTrace();            }            synchronized (flag) {                flag[0] = "false";                flag.notifyAll();            }        }    }    class WaitThread extends Thread {        public WaitThread(String name) {            super(name);        }        public void run() {            synchronized (flag) {                while (flag[0] != "false") {                    System.out.println(getName() + " begin waiting!");                    long waitTime = System.currentTimeMillis();                    try {                        flag.wait();                    } catch (InterruptedException e) {                        e.printStackTrace();                    }                    waitTime = System.currentTimeMillis() - waitTime;                    System.out.println("wait time :" + waitTime);                }                System.out.println(getName() + " end waiting!");            }        }    }}
Main Thread Run!waiter01 begin waiting!waiter02 begin waiting!waiter03 begin waiting!wait time :2999waiter03 end waiting!wait time :2999waiter02 end waiting!wait time :2999waiter01 end waiting!
View Code

 

=====================================================================================

wait和sleep的demo:

public class Main {    public static void main(String[] args) {        new Thread(new Thread1()).start();        try {            Thread.sleep(5000);        } catch (Exception e) {            e.printStackTrace();        }        new Thread(new Thread2()).start();    }    private static class Thread1 implements Runnable{        @Override        public void run(){            synchronized (Main.class) {                System.out.println("enter thread1...");                System.out.println("thread1 is waiting...");                try {                    //调用wait()方法,线程会放弃对象锁,进入等待此对象的等待锁定池                    Main.class.wait();                } catch (Exception e) {                    e.printStackTrace();                }                System.out.println("thread1 is going on ....");                System.out.println("thread1 is over!!!");            }        }    }    private static class Thread2 implements Runnable{        @Override        public void run(){            synchronized (Main.class) {                System.out.println("enter thread2....");                System.out.println("thread2 is sleep....");                //只有针对此对象调用notify()方法后本线程才进入对象锁定池准备获取对象锁进入运行状态。                Main.class.notify();                //==================                //区别                //如果我们把代码:Main.class.notify();给注释掉,即Main.class调用了wait()方法,但是没有调用notify()                //方法,则线程永远处于挂起状态。                try {                    //sleep()方法导致了程序暂停执行指定的时间,让出cpu该其他线程,                    //但是他的监控状态依然保持者,当指定的时间到了又会自动恢复运行状态。                    //在调用sleep()方法的过程中,线程不会释放对象锁。                    Thread.sleep(5000);                } catch (Exception e) {                    e.printStackTrace();                }                System.out.println("thread2 is going on....");                System.out.println("thread2 is over!!!");            }        }    }}
enter thread1...thread1 is waiting...enter thread2....thread2 is sleep....thread2 is going on....thread2 is over!!!thread1 is going on ....thread1 is over!!!
View Code

wait,notify的使用,线程同步唤醒

package com.qhong;/** * Created by Administrator on 2017/7/4 0004. */public class MyThreadPrinter2 implements Runnable {    private String name;    private Object prev;    private Object self;    private MyThreadPrinter2(String name, Object prev, Object self) {        this.name = name;        this.prev = prev;        this.self = self;    }    @Override    public void run() {        int count = 10;        while (count > 0) {            synchronized (prev) {                synchronized (self) {                    System.out.print(name);                    count--;                     self.notify();                }                try {                    prev.wait();                } catch (InterruptedException e) {                    e.printStackTrace();                }            }        }    }    public static void main(String[] args) throws Exception {        Object a = new Object();        Object b = new Object();        Object c = new Object();        MyThreadPrinter2 pa = new MyThreadPrinter2("A", c, a);        MyThreadPrinter2 pb = new MyThreadPrinter2("B", a, b);        MyThreadPrinter2 pc = new MyThreadPrinter2("C", b, c);        new Thread(pa).start();        Thread.sleep(1000);  //确保按顺序A、B、C执行        new Thread(pb).start();        Thread.sleep(1000);        new Thread(pc).start();        Thread.sleep(1000);    }}

Output:

ABCABCABCABCABCABCABCABCABCABC
View Code

Thread.sleep(100)是保证ABC的执行顺序

在这个例子中,可以深刻的体现出wait不仅是阻塞线程,更体现wait释放锁对象。

比如是4轮,第一轮的abc是有Thread.sleep来控制的,这个时候只有锁对象c是被释放的,锁对象a阻塞线程B,锁对象b阻塞线程C

                   第二轮,a的prev是c,所以是可以运行的,并且在这一步释放锁对象a,锁对象c注释线程A

                   ................

就这样一轮一轮的控制,虽然想通了,但是让我写还是想不出来的,挺繁琐!

有是一个demo

 

package com.qhong.thread;public class MyWaitNotify{    MonitorObject myMonitorObject = new MonitorObject();    boolean wasSignalled = false;    public void doWait(){        synchronized(myMonitorObject){            while(!wasSignalled){                try{                    myMonitorObject.wait();                } catch(InterruptedException e){...}            }            //clear signal and continue running.            wasSignalled = false;        }    }    public void doNotify(){        synchronized(myMonitorObject){            wasSignalled = true;            myMonitorObject.notify();        }    }}class MonitorObject{}

代码比较简单,但是要注意下面的说明,有好几个概念以前没注意:

1、不管是等待线程还是唤醒线程都在同步块里调用wait()和notify()。这是强制性的!一个线程如果没有持有对象锁,将不能调用wait(),notify()或者notifyAll()。否则,会抛出IllegalMonitorStateException异常。

2、一旦线程调用了wait()方法,它就释放了所持有的监视器对象上的锁。这将允许其他线程也可以调用wait()或者notify()。

3、为了避免丢失信号,必须把它们保存在信号类里。如上面的wasSignalled变量。

4、假唤醒:由于莫名其妙的原因,线程有可能在没有调用过notify()和notifyAll()的情况下醒来。这就是所谓的假唤醒(spurious wakeups)。为了防止假唤醒,保存信号的成员变量将在一个while循环里接受检查,而不是在if表达式里。这样的一个while循环叫做自旋锁。

5、不要在字符串常量或全局对象中调用wait()。即上面MonitorObject不能是字符串常量或是全局对象。每一个MyWaitNotify的实例都拥有一个属于自己的监视器对象,而不是在空字符串上调用wait()/notify()。

转载地址:http://zpesl.baihongyu.com/

你可能感兴趣的文章
智慧医疗在张家界的应用 首家智慧医疗医院投用
查看>>
Web 开发中 20 个很有用的 CSS 库
查看>>
Android系统中的进程管理:进程的创建
查看>>
看好大工业数据市场 GE将向软件业务14亿美元
查看>>
《JavaScript和jQuery实战手册(原书第2版)》——1.1节编程简介
查看>>
牛津大学量化金融创始人:如何获取并应用互联网大数据?
查看>>
快讯:特斯拉宣布26亿美元收购太阳能公司SolarCity
查看>>
你要知道的4个机房除尘小技巧
查看>>
百度金融布局金交所,账户和场景成掣肘
查看>>
仙童半导体拒绝华润等收购 担忧难获监管批准
查看>>
阿里视频云最强转码技术揭秘:窄带高清原理解析+用户接入指南
查看>>
茶道长:为什么加粉对于你的微商团队来说这么难?
查看>>
《安娜卡列尼娜》文本生成——利用 TensorFlow 构建 LSTM 模型
查看>>
《C语言程序设计》一 第 3 章 程序的控制结构Ⅰ——选择结构程序设计
查看>>
二十名工资最高的科技高管 仅有一家是纯安全公司
查看>>
《 嵌入式系统设计与实践》一一1.1 编译器、编程语言以及面向对象编程
查看>>
TensorFlow教程之完整教程 2.10 偏微分方程
查看>>
它是中国人口最小的城市,却美得像个意外!
查看>>
加码远程医疗 视频通信公司Vidyo获得医疗企业巨额投资
查看>>
高通输了官司,需返还黑莓8.15亿专利费
查看>>