Java 多线程 4:wait() 和 notify()/notifyAll()
轮询快速到底
线程本身是操作系统中独立的个体,但是线程与线程之间不是独立的个体,因为它们彼此之间要相互通信和协作。
想像一个场景,A 线程做 int 型变量 i 的累加操作,B 线程等待 i 到了 10000 就打印出i,怎么处理?一个办法就是,B 线程 while(i == 10000),这样两个线程之间就有了通信,B 线程不断通过轮训来检测 i == 10000 这个条件。
这样可以实现我们的需求,但是也带来了问题:CPU 把资源浪费了 B 线程的轮询操作上,因为 while 操作并不释放 CPU 资源,导致了 CPU 会一直在这个线程中做判断操作。如果可以把这些轮询的时间释放出来,给别的线程用,就好了。
wait/notify
在 Object 对象中有三个方法 wait()、notify()、notifyAll(),既然是 Object 中的方法,那每个对象自然都是有的。如果不接触多线程的话,这几个方法是不太常见的。下面看一下前两个方法:
1、wait()
wait() 的作用是使当前执行代码的线程进行等待,将当前线程进入阻塞状态,并且 wait() 所在的代码处停止执行,直到接到通知或被中断。在调用 wait() 之前,线程必须获得该对象的锁,因此只能在同步方法/同步代码块中调用 wait() 方法。
2、notify()
notify() 的作用是,如果有多个线程等待,那么线程规划器随机挑选出一个 wait 的线程,对其发出通知 notify(),并使它等待获取该对象的对象锁。注意"等待获取该对象的对象锁",这意味着,即使收到了通知,wait 的线程也不会马上获取对象锁,必须等待执行 notify() 方法的线程释放锁才可以。和 wait() 一样,notify() 也要在同步方法/同步代码块中调用。
总结起来就是,wait() 使线程停止运行,notify() 使停止运行的线程继续运行。
wait()/notify() 使用示例
看一个 Demo
public class MyThread extends Thread {private Object lock;public MyThread(Object lock){this.lock = lock;}public void run(){try{synchronized (lock){System.out.println("开始------wait time = " + System.currentTimeMillis());lock.wait();System.out.println("结束------wait time = " + System.currentTimeMillis());}}catch (InterruptedException e){e.printStackTrace();}} }public class MyThread_02 extends Thread {private Object lock;public MyThread_02(Object lock){this.lock = lock;}public void run(){synchronized (lock){System.out.println("开始------notify time = " + System.currentTimeMillis());lock.notify();System.out.println("结束------notify time = " + System.currentTimeMillis());}} }public static void main(String[] args) throws Exception {Object lock = new Object();MyThread mt = new MyThread(lock);mt.start();Thread.sleep(3000);MyThread_02 mt_02 = new MyThread_02(lock);mt_02.start(); }
运行结果
开始------wait time = 1443931599021开始------notify time = 1443931602024 // 从这个时间来看很明显上面执行 lock.wait() 方法的线程已经停止运行了(已经过了 3 毫秒了) 结束------notify time = 1443931602024结束------wait time = 1443931602024
解释:虽然上面执行了 lock.notify() 方法,但是一直等到执行 lock.notify() 方法的线程执行完 synchronized 代码块之后释放了对象锁,上面等待的线程才会拿到对象锁继续执行。
wait() 方法可以使执行该方法的线程释放共享资源的锁,然后从运行状态退出,进入等待队列,直到再次被唤醒。(其实就是锁被拿走了执行不了了)
notify() 方法可以随机唤醒等待队列中等待同一共享资源的一个线程,并使得该线程退出等待状态,进入可运行状态
notifyAll() 方法可以使所有正在等待队列中等待同一共享资源的全部线程从等待状态退出,进入可运行状态
最后,如果 wait() 方法和 notify()/notifyAll() 方法不在同步方法/同步代码块中被调用,那么虚拟机会抛出 java.lang.IllegalMonitorStateException,注意一下。
wait() 释放锁以及 notify() 不释放锁
多线程的学习中,任何地方都要关注"锁",wait() 和 notify()也是这样。wait() 方法是释放锁的,写一个例子来证明一下:
public class ThreadTest {public void testMethod(Object lock){try{synchronized (lock){System.out.println(Thread.currentThread().getName() + " 开始等待");lock.wait();System.out.println(Thread.currentThread().getName() + " 结束等待");}}catch (InterruptedException e){e.printStackTrace();}} } public class MyThread extends Thread {private Object lock;public MyThread(Object lock){this.lock = lock;}public void run(){ThreadTest td = new ThreadTest();td.testMethod(lock);} }public static void main(String[] args) {Object lock = new Object();MyThread mt0 = new MyThread(lock);MyThread mt1 = new MyThread(lock);mt0.start();mt1.start(); }
运行结果
Thread-0 开始等待 Thread-1 开始等待
如果 wait() 方法不释放锁,那么 Thread-1 根本不会进入同步代码块打印的,所以,证明完毕。
接下来证明一下 notify() 方法不释放锁的结论:
public class ThreadTest {public void testMethod(Object lock){try{synchronized (lock){System.out.println("开始等待, ThreadName = " + Thread.currentThread().getName());lock.wait();System.out.println("结束等待, ThreadName = " + Thread.currentThread().getName());}}catch (InterruptedException e){e.printStackTrace();}}public void synNotifyMethod(Object lock){try{synchronized (lock){System.out.println("开始 notify, ThreadName = " + Thread.currentThread().getName());lock.notify();Thread.sleep(5000);System.out.println("结束 notify, ThreadName = " + Thread.currentThread().getName());}}catch (InterruptedException e){e.printStackTrace();}} }public class MyThread extends Thread {private Object lock;public MyThread(Object lock){this.lock = lock;}public void run(){ThreadTest td = new ThreadTest();td.testMethod(lock);} }public class MyThread_02 extends Thread {private Object lock;public MyThread_02(Object lock){this.lock = lock;}public void run(){ThreadTest td = new ThreadTest();td.synNotifyMethod(lock);} }public static void main(String[] args) throws Exception {Object lock = new Object();MyThread mt0 = new MyThread(lock);mt0.start();MyThread_02 mt1 = new MyThread_02(lock);mt1.start();MyThread_02 mt2 = new MyThread_02(lock);mt2.start(); }
运行结果
1 开始等待, ThreadName = Thread-0 2 开始 notify(), ThreadName = Thread-1 3 结束 notify(), ThreadName = Thread-1 4 开始 notify(), ThreadName = Thread-2 5 结束 notify(), ThreadName = Thread-2 6 结束等待, ThreadName = Thread-0 // 这一步是有可能在第三步之后之后就拿到锁的
如果 notify() 方法释放锁,那么在 Thread-1 调用 notify() 方法后 Thread.sleep(5000) 必定应该有其他线程可以进入同步代码块了,但是实际上没有,必须等到Thread-1 把代码执行完。所以,证明完毕。
interrupt() 打断 wait()
之前有说过,interrupt() 方法的作用不是中断线程,而是在线程阻塞的时候给线程一个中断标识,表示该线程中断。wait() 就是"阻塞的一种场景",看一下用 interrupt() 打断 wait() 的例子:
public class ThreadTest {Object lock;public ThreadTest(Object lock){this.lock = lock;}public void show(){try{synchronized (lock){System.out.println("Begin wait() " + Thread.currentThread().getName());lock.wait();System.out.println("End wait() " + Thread.currentThread().getName());}} catch (InterruptedException e){System.out.println("wait()被interrupt()打断了!");e.printStackTrace();}}public static void main(String[] args) throws Exception{Object lock = new Object();MyThread myThread = new MyThread(lock);myThread.start();Thread.sleep(5000); // main 线程睡了 5 秒 myThread.interrupt();}}class MyThread extends Thread {Object lock;public MyThread(Object lock){this.lock = lock;}@Overridepublic void run(){ThreadTest threadTest = new ThreadTest(lock);threadTest.show();} }
看一下运行结果
Begin wait() Thread-0 wait()被interrupt()打断了! // 在打印这个之前暂停了 5 秒 java.lang.InterruptedExceptionat java.lang.Object.wait(Native Method)at java.lang.Object.wait(Object.java:503)at com.array.ThreadTest.show(ThreadTest.java:20)at com.array.MyThread.run(ThreadTest.java:56)
如果没有上面的 myThread.interupt(); 方法,那么五秒之后 myThread 还是处于“阻塞”的状态,那么整个程序还是卡在那里,红点还是亮着。换句话说,myThread.interupt(); 的作用是将处于“阻塞”状态的线程进行中断
notifyAll() 唤醒所有线程
利用 Object 对象的 notifyAll() 方法可以唤醒处于同一监视器下的所有处于 wait 的线程,看一个 Demo:
public class ThreadTest {public void testMethod(Object lock){try{synchronized (lock){System.out.println("Begin wait(), ThreadName = " + Thread.currentThread().getName());lock.wait();System.out.println("End wait(), ThreadName = " + Thread.currentThread().getName());}}catch (InterruptedException e){e.printStackTrace();}} } public class MyThread extends Thread {private Object lock;public MyThread(Object lock){this.lock = lock;}public void run(){ThreadTest td = new ThreadTest();td.testMethod(lock);} } public class MyThread_2 extends Thread {private Object lock;public MyThread_2(Object lock){this.lock = lock;}public void run(){synchronized (lock){lock.notifyAll();}} } public static void main(String[] args) throws Exception {Object lock = new Object();MyThread mt0 = new MyThread(lock);MyThread mt1 = new MyThread(lock);MyThread mt2 = new MyThread(lock);mt0.start();mt1.start();mt2.start();Thread.sleep(1000);MyThread_2 mt3 = new MyThread_2(lock);mt3.start();}
运行结果
Begin wait(), ThreadName = Thread-0 Begin wait(), ThreadName = Thread-2 Begin wait(), ThreadName = Thread-1 End wait(), ThreadName = Thread-1 End wait(), ThreadName = Thread-2 End wait(), ThreadName = Thread-0
当然,唤醒的顺序不重要,因为 notifyAll() 把处于同一资源下 wait 的线程全部唤醒,至于唤醒的顺序,就和线程启动的顺序一样,是虚拟机随机的。
转载于:https://www.cnblogs.com/tkzL/p/8909385.html
Java 多线程 4:wait() 和 notify()/notifyAll()相关推荐
- java多线程设计wait、notify、notifyall、synchronized的使用机制
wait.notify.notifyall.synchronized的使用机制: synchronized(obj) { while(!condition) { obj.wait(); } obj.d ...
- Java多线程---线程通信(wait,notifyAll,生产者消费者经典范式,owner wait set,自定义显式锁BooleanLock)
转自:https://blog.csdn.net/qq_35995514/article/details/91128585 1 学习内容 notifyAll 生产者.消费者经典范式 线程休息室 wai ...
- Java多线程之wait(),notify(),notifyAll()
在多线程的情况下,因为同一进程的多个线程共享同一片存储空间,在带来方便的同一时候,也带来了訪问冲突这个严重的问题.Java语言提供了专门机制以解决这样的冲突,有效避免了同一个数据对象被多个线程同一时候 ...
- Java多线程常用方法 wait 和 notify
一:从一道面试题说起 启动两个线程, 一个输出 1,3,5,7-99, 另一个输出 2,4,6,8-100 最后 STDOUT 中按序输出 1,2,3,4,5-100 要求用 Java 的 wait ...
- java线程同步(synchronized,wait,notify,notifyAll)
synchronized: 包括synchronized方法和synchronized块. synchronized方法使用this作为默认的"同步监视器",而synchroniz ...
- java多线程之wait_(三)java多线程之wait notify notifyAll
引言 今天我打算讲一下Object.wait,Object.notify,Object.notifyAll这三个方法. 首先我们查看一下api看看,官方api对这几个方法的介绍. 理论 Object. ...
- java 多线程 notify_Java多线程8:wait()和notify()/notifyAll()
轮询 线程本身是操作系统中独立的个体,但是线程与线程之间不是独立的个体,因为它们彼此之间要相互通信和协作. 想像一个场景,A线程做int型变量i的累加操作,B线程等待i到了10000就打印出i,怎么处 ...
- Java多线程中wait, notify and notifyAll的使用
本文为翻译文章,原文地址:http://www.journaldev.com/1037/java-thread-wait-notify-and-notifyall-example 在Java的Obje ...
- java 多线程 notifyall_java多线程之 wait(),notify(),notifyAll()
这几天在写一个java多线程服务器的功能,用到这些基础,自叹基础知识还需巩固,先写上一下这些说明,供自己和大家参考 wait(),notify(),notifyAll()不属于Thread类,而是属于 ...
最新文章
- GDCM:串联/提取DICOM文件的测试程序
- js导航条 二级滑动 模仿块级作用域
- 湖南省普通高等学校计算机应用水平,湖南省普通高等学校非计算机专业学生计算机应用水平二级考试大纲...
- 【华为云技术分享】《跟唐老师学习云网络》 - 我的网络概念
- Google I/O 2018 之后, Android 工程师将何去何从?
- STM32工作笔记0032---编写跑马灯实验---寄存器版本
- 二、Python自动化运维入门(函数、模块)
- 基于php+Mysql新闻管理系统 开题报告
- 【Proteus仿真】51单片机驱动蜂鸣器播放《天空之城》
- 常用网络端口用处归纳
- ensp服务器配置文件,ensp配置web服务器
- 射频识别(RFID)
- 最新BBS上的变态网名大全
- 大数据之实战足球盘口、凯利、必发和预测结果分析
- origin画图---学习时遇到的画图记录
- 英文文本分类——电影评论情感判别
- Python开发:PyQT安装教程
- Android之TextView
- 刚进职场的程序员,和工作了2、3年的程序员到底有什么不一样?
- 洛谷P1607 [USACO09FEB]庙会班车Fair Shuttle
热门文章
- css 动态rem_HTML + CSS 为何得不到编程界的认可?
- OnePlus是什么手机
- htaccess有什么用
- 有些卖花生的人6.5元拿货,却卖6元,这是怎么回事?求解?
- 大学计算机老师说未来不需要程序员,都是机器自动生成代码。老师说估计20年,程序员就会被取代,现实吗?
- 铁路从未授权任何第三方平台售票服务,各大旅游网站哪来的接口?
- radvd移植到arm交叉编译问题解决
- [leetcode] Median of Two Sorted Arrays 寻找两个有序数组的中位数
- sql 除以_避免SQL除以零错误的方法
- sql 标量子查询_SQL Server 2017:标量子查询简化