转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/119645679
本文出自【赵彦军的博客】

文章目录

  • wait 和 notify 简介
  • 对象控制权(monitor)
  • 解题
  • 发散:notify()和notifyAll()
  • 扩展

最近看帖子,发现一道面试题:

启动两个线程, 一个输出 1,3,5,7…99, 另一个输出 2,4,6,8…100 最后 STDOUT 中按序输出 1,2,3,4,5…100

题目要求用 Java 的 wait + notify 机制来实现,重点考察对于多线程可见性的理解。

wait 和 notify 简介

wait 和 notify 均为 Object 的方法:

  • Object.wait() —— 暂停一个线程
  • Object.notify() —— 唤醒一个线程

从以上的定义中,我们可以了解到以下事实:

  • 想要使用这两个方法,我们需要先有一个对象 Object。
  • 在多个线程之间,我们可以通过调用同一个对象的wait()和notify()来实现不同的线程间的可见。

对象控制权(monitor)

在使用 wait 和 notify 之前,我们需要先了解对象的控制权(monitor)。在 Java 中任何一个时刻,对象的控制权只能被一个线程拥有。如何理解控制权呢?请先看下面的简单代码:

public class Util {public void run(){Object ob = new Object();new Thread(new Runnable() {@Overridepublic void run() {try {ob.wait();} catch (InterruptedException e) {e.printStackTrace();}}}).start();}
}

直接执行,我们将会得到以下异常:

E/AndroidRuntime: FATAL EXCEPTION: Thread-3Process: com.example.myapplication, PID: 12211java.lang.IllegalMonitorStateException: object not locked by thread before wait()at java.lang.Object.wait(Native Method)at java.lang.Object.wait(Object.java:442)at java.lang.Object.wait(Object.java:568)at com.example.myapplication.Util$1.run(Util.java:20)at java.lang.Thread.run(Thread.java:929)

出错的代码在:object.wait();。这里我们需要了解以下事实:

  • 无论是执行对象的 wait、notify 还是 notifyAll 方法,必须保证当前运行的线程取得了该对象的控制权(monitor)
  • 如果在没有控制权的线程里执行对象的以上三种方法,就会报 java.lang.IllegalMonitorStateException 异常。
  • JVM 基于多线程,默认情况下不能保证运行时线程的时序性

在上面的示例代码中,我们 new 了一个 Thread,但是对象 object 的控制权仍在主线程里。所以会报 java.lang.IllegalMonitorStateException 。

我们可以通过同步锁来获得对象控制权,例如:synchronized 代码块。对以上的示例代码做改造:

public class Util {public void run(){Object ob = new Object();new Thread(new Runnable() {@Overridepublic void run() {synchronized (ob){try {ob.wait();} catch (InterruptedException e) {e.printStackTrace();}}}}).start();}
}

再次执行,代码不再报错。

我们可以得到以下结论:

  • 调用对象的wait()和notify()方法,需要先取得对象的控制权
  • 可以使用synchronized (object)来取得对于 object 对象的控制权

解题

了解了对象控制权之后,我们就可以正常地使用 notify 和 wait 了,下面给出我的解题方法,供参考。

方法一:

public class Util {public void run(){Object ob = new Object();new Thread(new Runnable() {@Overridepublic void run() {synchronized (ob){try {for (int i = 0 ;i < 100; i=i+2){ob.notify();Log.d("num--", "" + i); //偶数ob.wait();}} catch (InterruptedException e) {e.printStackTrace();}}}}).start();new Thread(new Runnable() {@Overridepublic void run() {synchronized (ob){try {for (int i = 1 ;i < 100; i=i+2){ob.notify();Log.d("num------", "" + i);  //奇数ob.wait();}} catch (InterruptedException e) {e.printStackTrace();}}}}).start();}
}

方法二:

public class Util {public void run(){Object ob = new Object();new Thread(new Runnable() {@Overridepublic void run() {synchronized (ob){try {for (int i = 0 ;i < 100; i=i+2){Log.d("num--", "" + i); //偶数ob.notifyAll();ob.wait();}} catch (InterruptedException e) {e.printStackTrace();}}}}).start();new Thread(new Runnable() {@Overridepublic void run() {synchronized (ob){try {for (int i = 1 ;i < 100; i=i+2){Log.d("num------", "" + i);  //奇数ob.notifyAll();ob.wait();}} catch (InterruptedException e) {e.printStackTrace();}}}}).start();}
}

发散:notify()和notifyAll()

这两个方法均为 native 方法,在JDK 1.8 中的关于notify()的JavaDoc如下:

Wakes up a single thread that is waiting on this object’s monitor. If any threads are waiting on this object, one of them is chosen to be awakened.

译为:

唤醒此 object 控制权下的一个处于 wait 状态的线程。若有多个线程处于此 object 控制权下的 wait 状态,只有一个会被唤醒。

也就是说,如果有多个线程在 wait 状态,我们并不知道哪个线程会被唤醒。

在JDK 1.8 中的关于notifyAll()的JavaDoc如下:

Wakes up all threads that are waiting on this object’s monitor.

译为:

唤醒所有处于此 object 控制权下的 wait 状态的线程。

所以,我们需要根据实际的业务场景来考虑如何使用。

扩展

关于对象锁,notify只是唤醒一个线程B,B这个线程要等到当前对象释放synchronized的对象锁才能执行,也就是A flag.wait()才能执行。再用高级点的说法,就是notify是等待池进入锁池。

wait()方法 表示持有对象锁的线程A准备释放对象锁权限,释放CPU资源并进入等待。

Wait() 和notify() 方法只能从synchronized方法或块中调用,需要在其他线程正在等待的对象上调用notify方法。

notifyAll 通知JVM唤醒所有竞争该对象锁的线程,线程A synchronized 代码作用域结束后,JVM通过算法将对象锁权限指派给某个线程X,所有被唤醒的线程不再等待。线程X synchronized 代码作用域结束后,之前所有被唤醒的线程都有可能获得该对象锁权限,这个由JVM算法决定。

通俗比喻:

有两个人A和B都要和一个女孩G约会(A线程和B线程都调用synchronized约会方法,锁对象G.class)。

A率先和G约会了(A线程进入synchronized方法),B只能等着。

A在约会途中被电话叫走,把G凉在那里(调用G.class.wait())。

这时候B发现G没人约会了,于是上场(B线程进入synchronized方法)。

等到B和G约会完成,B又打电话叫A回来继续约会(B线程调用G.class.notify()方法),A才回来继续约会直到完成。

notifyAll()就是不止A和B了,可能有更多的人要和G约会,等A走了之后某个人上场。

JAVA多线程中wait()方法的详细分析相关推荐

  1. JAVA多线程中join()方法的详细分析

    虽然关于讨论线程join()方法的博客已经非常极其特别多了,但是前几天我有一个困惑却没有能够得到详细解释,就是当系统中正在运行多个线程时,join()到底是暂停了哪些线程,大部分博客给的例子看起来都像 ...

  2. 【Java之多线程(二)】(***重要***)Java多线程中常见方法的区别,如object.wait()和Thread.sleep()的区别等

    1.Java中Thread和Runnable的区别??? 区别: 在程序开发中只要是多线程肯定永远以实现Runnable接口为主,因为实现Runnable接口相比继承Thread类有如下好处: 避免点 ...

  3. Java多线程中notifyAll()方法使用教程

    简介 本文将承接<Java多线程wait()和notify()系列方法使用教程>,结合代码实例,补充讲解下notifyAll()方法的作用以及使用时需要注意的地方. 一.notifyAll ...

  4. Java多线程中join方法详解

    join()方法用于让当前执行线程等待join线程执行结束.其实现原理是不停的检查join线程是否存活,如果join线程存活则让当前线程永远等待. join()方法部分实现细节 while(isAli ...

  5. java面试中jvm执行子系统详细分析(三)

    什么是jvm? JVM是Java Virtual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的 ...

  6. java多线程中的join方法详解

    java多线程中的join方法详解 方法Join是干啥用的? 简单回答,同步,如何同步? 怎么实现的? 下面将逐个回答. 自从接触Java多线程,一直对Join理解不了.JDK是这样说的:join p ...

  7. 转:Java多线程学习(总结很详细!!!)

    Java多线程学习(总结很详细!!!) 此文只能说是java多线程的一个入门,其实Java里头线程完全可以写一本书了,但是如果最基本的你都学掌握好,又怎么能更上一个台阶呢? 本文主要讲java中多线程 ...

  8. java多线程同步与死锁_浅析Java多线程中的同步和死锁

    Value Engineering 1基于Java的多线程 多线程是实现并发机制的一种有效手段,它允许编程语言在程序中并发执行多个指令流,每个指令流都称为一个线程,彼此间相互独立,且与进程一样拥有独立 ...

  9. thymeleaf 调用java,thymeleaf模板引擎调用java类中的方法(附源码)

    前言 由于开源了项目的缘故,很多使用了My Blog项目的朋友遇到问题也都会联系我去解决,有的是把问题留在项目的issue里提出,有的是在我的私人博客里留言,还有的则是直接添加我的qq来找我讲自己遇到 ...

最新文章

  1. NLP经典算法复现!CRF原理及实现代码
  2. IDC报告称经济衰退将促进云计算发展
  3. 2019\Province_C_C++_B\试题A-组队
  4. linux目录树(书本上看到)
  5. android源码分析(一) - 语言切换机制
  6. ImportError: No module named ‘BaseHTTPServer‘:解决方案
  7. python生成wps文件_使用Python操作XLS文件(wps中叫et)
  8. 190513每日一句
  9. 菜鸟抓鸡--各个端口的***总结
  10. 从零开始学习CANoe(六)—— CAPL 测试节点
  11. git密匙的创建与git的基本操作
  12. Python计算最大回撤、回撤天数
  13. 文件管理之文件和文件系统
  14. db2 replace函数的用法_总结篇--SUBSTITU函数实用终极帖
  15. 使用正则批量修改文件名
  16. (三)RabbitMQ集群(Ⅰ)
  17. 2020年全球EDA软件行业市场竞争格局分析 三巨头三足鼎立
  18. 原来 8 张图,就可以搞懂「零拷贝」了
  19. Php freeradius 认证,搭建FreeRadius实现远程登录认证(第一部分)
  20. HTML5期末大作业:旅游景点网站设计——成都(6页) HTML+CSS+JavaScript 大学生家乡网页设计作业模板下载 四川成都城市网页设计作业成品 静态HTML旅游景点网页制作下载...

热门文章

  1. mt6765和骁龙665哪个好_小米11正式发布,首发骁龙888+白送快充头,售价3999起
  2. java过滤4字节_乐字节Java8核心特性实战之四:方法引用
  3. java创建多级xml树_月光软件站 - 编程文档 - Java - 用XSL与XML实现多级树形菜单
  4. 计算机专业开学周记,【热门】开学周记集锦5篇
  5. 道格拉斯算法 java_道格拉斯-普克算法的java的实现代码如下
  6. idea启动webservice_idea使用springboot的webservice基于cxf
  7. 原来Python破解受密码保护的zip文件这么简单,不保证一定成功
  8. 如何将因果干预用于提升模型公平性?
  9. IEEE Transactions on BIG DATA影响因子3.344,进入信息学科计算机理论与方法领域Q1区...
  10. Prompt-based Language Models:模版增强语言模型小结