wait()、notify()、notifyAll()是三个定义在Object类里的方法,可以用来控制线程的状态。

这三个方法最终调用的都是jvm级的native方法。随着jvm运行平台的不同可能有些许差异。

  • 如果对象调用了wait方法就会使持有该对象的线程把该对象的控制权交出去,然后处于等待状态。
  • 如果对象调用了notify方法就会通知某个正在等待这个对象的控制权的线程可以继续运行。
  • 如果对象调用了notifyAll方法就会通知所有等待这个对象控制权的线程继续运行。

其中wait方法有三个over load方法:

wait()

wait(long)

wait(long,int)

wait方法通过参数可以指定等待的时长。如果没有指定参数,默认一直等待直到被通知。

以下是一个演示代码,以最简洁的方式说明复杂的问题:

简要说明下:

NotifyThread是用来模拟3秒钟后通知其他等待状态的线程的线程类;

WaitThread是用来模拟等待的线程类;

等待的中间对象是flag,一个String对象;

main方法中同时启动一个Notify线程和三个wait线程;

public class NotifyTest {  private  String flag = "true";  class NotifyThread extends Thread{  public NotifyThread(String name) {  super(name);  }  public void run() {       try {  sleep(3000);//推迟3秒钟通知  } catch (InterruptedException e) {  e.printStackTrace();  }  flag = "false";  flag.notify();  }  };  class WaitThread extends Thread {  public WaitThread(String name) {  super(name);  }  public void run() {  while (flag!="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!");  }  }  public static void main(String[] args) throws InterruptedException {  System.out.println("Main Thread Run!");  NotifyTest test = new NotifyTest();  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();  }  }  

OK,如果你拿这段程序去运行下的话, 会发现根本运行不了,what happened?满屏的java.lang.IllegalMonitorStateException。

没错,这段程序有很多问题,我们一个个来看。

首先,这儿要非常注意的几个事实是

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

基于以上几点事实,我们需要确保让线程拥有对象的控制权。

也就是说在waitThread中执行wait方法时,要保证waitThread对flag有控制权;

在notifyThread中执行notify方法时,要保证notifyThread对flag有控制权。

线程取得控制权的方法有三:

  1. 执行对象的某个同步实例方法。
  2. 执行对象对应类的同步静态方法。
  3. 执行对该对象加同步锁的同步块。
我们用第三种方法来做说明:
将以上notify和wait方法包在同步块中
synchronized (flag) {  flag = "false";  flag.notify();  }  

synchronized (flag) {  while (flag!="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!");  }  

我们向前进了一步。

问题解决了吗?

好像运行还是报错java.lang.IllegalMonitorStateException。what happened?

这时的异常是由于在针对flag对象同步块中,更改了flag对象的状态所导致的。如下:

flag="false";

flag.notify();

对在同步块中对flag进行了赋值操作,使得flag引用的对象改变,这时候再调用notify方法时,因为没有控制权所以抛出异常。

我们可以改进一下,将flag改成一个JavaBean,然后更改它的属性不会影响到flag的引用。

我们这里改成数组来试试,也可以达到同样的效果:

rivate   String flag[] = {"true"};  

synchronized (flag) {  flag[0] = "false";  flag.notify();  }  

synchronized (flag) {  while (flag[0]!="false") {  System.out.println(getName() + " begin waiting!");  long waitTime = System.currentTimeMillis();  try {  flag.wait();  } catch (InterruptedException e) {  e.printStackTrace();  }  

这时候再运行,不再报异常,但是线程没有结束是吧,没错,还有线程堵塞,处于wait状态。

原因很简单,我们有三个wait线程,只有一个notify线程,notify线程运行notify方法的时候,是随机通知一个正在等待的线程,所以,现在应该还有两个线程在waiting。

我们只需要将NotifyThread线程类中的flag.notify()方法改成notifyAll()就可以了。notifyAll方法会通知所有正在等待对象控制权的线程。

最终完成版如下:

public class NotifyTest {  private String flag[] = { "true" };  class NotifyThread extends Thread {  public NotifyThread(String name) {  super(name);  }  public void run() {  try {  sleep(3000);  } 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!");  }  }  }  public static void main(String[] args) throws InterruptedException {  System.out.println("Main Thread Run!");  NotifyTest test = new NotifyTest();  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();  }  }  

转自:http://longdick.iteye.com/blog/453615

转载于:https://www.cnblogs.com/guweiwei/p/6556404.html

【Java 线程的深入研究3】最简单实例说明wait、notify、notifyAll的使用方法相关推荐

  1. Java程序如何写判断闰年_用Java程序判断是否是闰年的简单实例

    我们知道,(1)如果是整百的年份,能被400整除的,是闰年:(2)如果不是整百的年份,能被4整除的,也是闰年.每400年,有97个闰年.鉴于此,程序可以作以下设计: 第一步,判断年份是否被400整除, ...

  2. java数字编程提,java从字符串中提取数字的简单实例

    随便给你一个含有数字的字符串,比如: String s="eert343dfg56756dtry66fggg89dfgf"; 那我们怎么把其中的数字提取出来呢?大致有以下几种方法, ...

  3. 编写java判断闰年_用Java程序判断是否是闰年的简单实例

    我们知道,(1)如果是整百的年份,能被400整除的,是闰年:(2)如果不是整百的年份,能被4整除的,也是闰年.每400年,有97个闰年.鉴于此,程序可以作以下设计: 第一步,判断年份是否被400整除, ...

  4. Java线程死锁–案例研究

    本文将描述从在IBM JVM 1.6上运行的Weblogic 11g生产系统中观察到的最新Java死锁问题的完整根本原因分析. 此案例研究还将证明掌握线程转储分析技能的重要性: 包括用于IBM JVM ...

  5. Java 线程的6种状态(简单介绍)

    关于Java线程的状态网上的说法不一,有的说是6种状态,有的说是5中状态,索性就查看了一下Java源码.Thread类里面有一个枚举类,如下: public enum State {NEW,RUNNA ...

  6. 【Java 线程池 概念+深析】简单理解

    加油,每天一篇博客,听一遍好运来 目录 1.简介 2.线程池 2.1 线程池的作用 2.2 为什么要用线程池 3.线程池的创建 3.1 线程池实例 3.1 四种线程池的使用 3.2 线程池实现原理 4 ...

  7. java线程开启不了_Java中多线程启动,为什么调用的是start方法,而不是run方法?...

    前言 大年初二,大家新年快乐,我又开始码字了.写这篇文章,源于在家和基友交流的时候,基友问到了,我猛然发现还真是这么回事,多线程启动调用的都是start,那么为什么没人掉用run呢?于是打开我的ide ...

  8. java webserver demo_Java 实现 web服务器的简单实例

    Java 实现 web服务器的简单实例 实例代码: import java.util.*; // Chapter 8, Listing 3 public class WebServerDemo { / ...

  9. Java的反射机制,内含超简单实例代码(搞懂反射,这一篇就够了)

    一 首先来说说反射机制的概念: 程序在运行时, 对于类来说,可以知道该类的任意属性和方法: 对于对象来说,可以调用该对象的任意方法和属性: 就以上这种动态获取信息的机制就称为Java的反射机制 彻底了 ...

最新文章

  1. STM32向量表详细分析
  2. spring aop 如何切面到mvc 的controller--转载
  3. k8s Service之LoadBalancer和ExternalName
  4. 记一次synchronized锁字符串引发的坑兼再谈Java字符串
  5. Pensando Distributed Services Architecture [Pensando 分布式服务架构] - 翻译
  6. mysql表的relationship_sqlalchemy 配置多连接读写库后的relationship设置
  7. 阶段3 3.SpringMVC·_06.异常处理及拦截器_7 SpringMVC拦截器之拦截器接口方法演示
  8. 基于源码编译安装openssh
  9. 软件的黑盒和白盒分析方法
  10. 看完这篇就够了,mac版本最新Camera Raw 15.1 新功能HDR详解
  11. android客服功能实现,基于环信实现android客户端客服聊天功能
  12. 私货——OIer 必备网站集
  13. WSL与idea集成攻略
  14. 【Java学习之代码学习】 Prog28_打印出杨辉三角形的问题
  15. dbeaver连接hive3.1.2,不需添加驱动
  16. LeetCode OJ 之 Number of 1 Bits (二进制位1的个数)
  17. Walle 瓦力 安装部署
  18. 计算机国际会议开幕词,国际学术会议开幕词.doc
  19. 只有安卓系统的浏览器提示证书不受信任的问题 ----- 证书链不完整的解决办法
  20. 电脑控制手机/手机控制手机/手机控制电脑

热门文章

  1. android 移除自己view,Android自定义View-带删除和搜索图标的EditText
  2. 郑州网络推广浅谈网站首页在优化时都需要注意哪些细节呢?
  3. 网络品牌推广之标签的使用注意事项
  4. 网络营销外包期间如何提升网络营销外包外链优化效果?
  5. 小学五年级计算机进度安排,五年级下册信息技术年度教学计划
  6. centos mysql5.7主从同步配置_centos7搭建mysql5.7主从同步
  7. 高二下学期计算机考试试题及答案,福建省泉州市第五高级中学校2020-2021学年高二下学期期中考试数学试题含答案.docx...
  8. 检验入参合法性有哪些_验证用户输入的参数合法性的shell脚本
  9. 趣谈网络协议笔记-二(第六讲)
  10. 一些蠕虫传播研究的文章——TODO