Thread之七:Object里的wait、notify、notifyAll的使用方法
wait()、notify()、notifyAll()是三个定义在Object类里的方法,可以用来控制线程的状态
- public final native void notify();
- public final native void notifyAll();
- public final native void wait(long l)
- throws InterruptedException;
- public final void wait(long l, int i)
- throws InterruptedException
- {
- if(l < 0L)
- throw new IllegalArgumentException("timeout value is negative");
- if(i < 0 || i > 999999)
- throw new IllegalArgumentException("nanosecond timeout value out of range");
- if(i > 0)
- l++;
- wait(l);
- }
- public final void wait()
- throws InterruptedException
- {
- wait(0L);
- }
这三个方法最终调用的都是jvm级的final native方法。随着jvm运行平台的不同可能有些许差异。
- 如果对象调用了wait方法就会使持有该对象的线程把该对象的控制权交出去,然后处于等待状态。
- 如果对象调用了notify方法就会通知某个正在等待这个对象的控制权的线程可以继续运行。
- 如果对象调用了notifyAll方法就会通知所有等待这个对象控制权的线程继续运行。
其中wait方法有三个over load方法:
wait()
wait(long)
wait(long,int)
wait方法通过参数可以指定等待的时长。如果没有指定参数,默认一直等待直到被通知。
以下是一个演示代码,以最简洁的方式说明复杂的问题:
简要说明下:
NotifyThread是用来模拟3秒钟后通知其他等待状态的线程的线程类;
WaitThread是用来模拟等待的线程类;
等待的中间对象是flag,一个String对象;
main方法中同时启动一个Notify线程和三个wait线程;
- package com.dxz.synchronizeddemo;
- 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() {
- System.out.println(getName() + " flag:" + flag);
- while (!flag.equals("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();
- }
- }
结果:
- Main Thread Run!
- Exception in thread "waiter03" waiter03 flag:true
- waiter02 flag:true
- waiter03 begin waiting!
- waiter01 flag:true
- waiter02 begin waiting!
- waiter01 begin waiting!
- Exception in thread "waiter02" Exception in thread "waiter01" java.lang.IllegalMonitorStateException
- at java.lang.Object.wait(Native Method)
- at java.lang.Object.wait(Object.java:502)
- at com.dxz.synchronizeddemo.NotifyTest$WaitThread.run(NotifyTest.java:34)
- java.lang.IllegalMonitorStateException
- at java.lang.Object.wait(Native Method)
- at java.lang.Object.wait(Object.java:502)
- at com.dxz.synchronizeddemo.NotifyTest$WaitThread.run(NotifyTest.java:34)
- java.lang.IllegalMonitorStateException
- at java.lang.Object.wait(Native Method)
- at java.lang.Object.wait(Object.java:502)
- at com.dxz.synchronizeddemo.NotifyTest$WaitThread.run(NotifyTest.java:34)
- Exception in thread "notify01" java.lang.IllegalMonitorStateException
- at java.lang.Object.notify(Native Method)
- at com.dxz.synchronizeddemo.NotifyTest$NotifyThread.run(NotifyTest.java:19)
在wait和notify的地方都报错java.lang.IllegalMonitorStateException,前面也讲过,wait和notify方法一定要在synchronized里面,更具体点说有:
- 任何一个时刻,对象的控制权(monitor)只能被一个线程拥有。
- 无论是执行对象的wait、notify还是notifyAll方法,必须保证当前运行的线程取得了该对象的控制权(monitor)
- 如果在没有控制权的线程里执行对象的以上三种方法,就会报java.lang.IllegalMonitorStateException异常。
- JVM基于多线程,默认情况下不能保证运行时线程的时序性
基于以上几点事实,我们需要确保让线程拥有对象的控制权。
也就是说在waitThread中执行wait方法时,要保证waitThread对flag有控制权;
在notifyThread中执行notify方法时,要保证notifyThread对flag有控制权。
线程取得控制权的方法有三:见《Synchronized之一:基本使用》
- 同步的实例方法(锁用的是其实例对象本身。所有的非静态同步方法执行需要顺序执行,即不能并行执行。)
- 同步的静态方法(锁用的是其类对象本身。所有的静态同步方法执行需要顺序执行,即不能并行执行。)
- 实例方法中的同步块(锁是自己指定的,但不能是引用性对象及null对象)
- 静态方法中的同步块(锁是自己指定的,但不能是引用性对象及null对象)
- //nofity放入synchronized中
- synchronized (flag) {
- flag = "false";
- flag.notify();
- }
- //wait放入synchronized中
- synchronized (flag) {
- while (!flag.equals("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);
- }
- }
再运行的结果:
Main Thread Run!
waiter02 flag:true
waiter01 flag:true
waiter03 flag:true
waiter02 begin waiting!
waiter03 begin waiting!
waiter01 begin waiting!
Exception in thread "notify01" java.lang.IllegalMonitorStateException
at java.lang.Object.notify(Native Method)
at com.dxz.synchronizeddemo.NotifyTest$NotifyThread.run(NotifyTest.java:20)
在flag.notify();的地方还是会报错java.lang.IllegalMonitorStateException。这时的异常是由于在针对flag对象同步块中,更改了flag对象的状态所导致的。如下:
- flag="false"; //改变的对象的内容
- flag.notify();
对在同步块中对flag进行了赋值操作,使得flag引用的对象改变,这时候再调用notify方法时,因为没有控制权所以抛出异常。
我们可以改进一下,将flag改成一个JavaBean,然后更改它的属性不会影响到flag的引用。
我们这里改成数组来试试,也可以达到同样的效果:
这时候再运行,不再报异常,但是线程没有结束是吧,没错,还有线程堵塞,处于wait状态。
原因很简单,我们有三个wait线程,只有一个notify线程,notify线程运行notify方法的时候,是随机通知一个正在等待的线程,所以,现在应该还有两个线程在waiting。
我们只需要将NotifyThread线程类中的flag.notify()方法改成notifyAll()就可以了。notifyAll方法会通知所有正在等待对象控制权的线程。
最终完成版如下:
- package com.dxz.synchronizeddemo;
- public class NotifyTest2 {
- 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();
- }
- synchronized (flag) {
- flag[0] = "false";
- flag.notify();
- }
- }
- };
- class WaitThread extends Thread {
- public WaitThread(String name) {
- super(name);
- }
- public void run() {
- synchronized (flag) {
- System.out.println(getName() + " flag:" + flag);
- while (!flag[0].equals("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!");
- NotifyTest2 test = new NotifyTest2();
- 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();
- }
- }
notify()和notifyAll()的本质区别
notify()和notifyAll()都是Object对象用于通知处在等待该对象的线程的方法。两者的最大区别在于:
notifyAll使所有原来在该对象上等待被notify的所有线程统统退出wait的状态,变成等待该对象上的锁,一旦该对象被解锁,他们就会去竞争。
notify则文明得多,它只是选择一个wait状态线程进行通知,并使它获得该对象上的锁,但不惊动其他同样在等待被该对象notify的线程们,当第一个线程运行完毕以后释放对象上的锁此时如果该对象没有再次使用notify语句,则即便该对象已经空闲,其他wait状态等待的线程由于没有得到该对象的通知,继续处在wait状态,直到这个对象发出一个notify或notifyAll,它们等待的是被notify或notifyAll,而不是锁。
下面是一个很好的例子:
- package com.dxz.synchronizeddemo;
- public class NotifyTest3 {
- 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();
- }
- synchronized (flag) {
- flag[0] = "false";
- flag.notifyAll();
- }
- }
- };
- class WaitThread extends Thread {
- public WaitThread(String name) {
- super(name);
- }
- public void run() {
- System.out.println(getName() + " flag:" + flag);
- synchronized (flag) {
- System.out.println(getName() + " flag:" + flag);
- while (!flag[0].equals("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!");
- }
- System.out.println(getName() + " end waiting!");
- }
- }
- public static void main(String[] args) throws InterruptedException {
- System.out.println("Main Thread Run!");
- NotifyTest3 test = new NotifyTest3();
- 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();
- }
- }
结果:
- Main Thread Run!
- waiter01 flag:[Ljava.lang.String;@584aceca
- waiter01 flag:[Ljava.lang.String;@584aceca
- waiter02 flag:[Ljava.lang.String;@584aceca
- waiter01 begin waiting!
- waiter02 flag:[Ljava.lang.String;@584aceca
- waiter02 begin waiting!
- waiter03 flag:[Ljava.lang.String;@584aceca
- waiter03 flag:[Ljava.lang.String;@584aceca
- waiter03 begin waiting!
- wait time :3000
- waiter03 end waiting!
- waiter03 end waiting!
- wait time :3001
- waiter02 end waiting!
- waiter02 end waiting!
- wait time :3001
- waiter01 end waiting!
- waiter01 end waiting!
Thread之七:Object里的wait、notify、notifyAll的使用方法的更多相关文章
- Object中的wait,notify,notifyAll基本使用(转)
让线程停止运行/睡眠的方法只有两个:Thread.sleep()或者obj.wait() 记住obj.nofity()并不能停止线程运行,因为notify虽然释放了锁,但依然会急促执行完synchro ...
- Java Object对象中的wait,notify,notifyAll的理解
wait,notify,notifyAll 是定义在Object类的实例方法,用于控制线程状态,在线程协作时,大家都会用到notify()或者notifyAll()方法,其中wait与notify是j ...
- Thread(生产者和消费者) wait、notify、notifyAll
在java中,线程间的通信可以使用wait.notify.notifyAll来进行控制.从名字就可以看出来这3个方法都是跟多线程相关的,但是可能让你感到吃惊的是:这3个方法并不是Thread类或者是R ...
- java 多线程之wait(),notify,notifyAll(),yield()
wait(),notify(),notifyAll()不属于Thread类,而是属于Object基础类,也就是说每个对像都有wait(),notify(),notifyAll()的功能.因为都个对像都 ...
- java 为什么wait(),notify(),notifyAll()必须在同步方法/代码块中调用?
在Java中,所有对象都能够被作为"监视器monitor"——指一个拥有一个独占锁,一个入口队列和一个等待队列的实体entity.所有对象的非同步方法都能够在任意时刻被任意线程调用 ...
- java中的wait(),notify(),notifyAll(),synchronized方法
wait(),notify(),notifyAll()三个方法不是Thread的方法,而是Object的方法.意味着所有对象都有这三个方法,因为每个对象都有锁,所以自然也都有操作锁的方法了.这三个方法 ...
- Java多线程之wait(),notify(),notifyAll()
在多线程的情况下,因为同一进程的多个线程共享同一片存储空间,在带来方便的同一时候,也带来了訪问冲突这个严重的问题.Java语言提供了专门机制以解决这样的冲突,有效避免了同一个数据对象被多个线程同一时候 ...
- Java多线程的wait(),notify(),notifyAll()
在多线程的情况下.因为多个线程与存储空间共享相同的过程,同时带来的便利.它也带来了访问冲突这个严重的问题. Java语言提供了一种特殊的机制来解决这类冲突,避免同一数据对象由多个线程在同一时间访问. ...
- 多线程学习-基础(六)分析wait()-notify()-notifyAll()
一.理解wait()-notify()-notifyAll()obj.wait()与obj.notify()必须要与synchronized(Obj)一起使用,也就是wait,notify是针对已经获 ...
- Java多线程:线程状态以及wait(), notify(), notifyAll()
一. 线程状态类型1. 新建状态(New):新创建了一个线程对象.2. 就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法.该状态的线程位于可运行线程池中,变得可运 ...
随机推荐
- 重新想,重新看——CSS3变形,过渡与动画②
本篇文章主要用来归纳总结CSS3变形属性. CSS3变形属性大致可以分为以下三个部分: 变形控制属性 2D变形函数 3D变形函数 下面将对其一一进行分析: 1.变形控制属性 所谓的变形控制属性主要指“ ...
- mysql对数据库的备份和还原
在对mysql数据库的某个数据库进行备份时,使用 mysqldump命令来进行操作 mysqldump -u root -p db_database_name > /[your_path.mys ...
- Redis之Redis
Redis 环境安装 安装 如果已经安装了老版本3.0.6 1. 卸载软件 sudo apt-get remove redis-server 2. 清除配置 sudo apt-get remove - ...
- LeetCode——4Sum
1. Question Given an array S of n integers, are there elements a, b, c, and d in S such that a + b + ...
- LeetCode——remove-duplicates-from-sorted-list-ii
Question Given a sorted linked list, delete all nodes that have duplicate numbers, leaving only dist ...
- Docker-Compose 一键部署Ningx+.Net Core+Redis集群
在看该文章前,你需要对Docker有所了解. 1.创建WebApp应用程序 我使用的是.Net Core 1.0.1版本,创建一个MVC应用程序,并添加对Redis的引用.因为这些很基础,也很简单,这 ...
- 01_zookeeper简介(刷新)
1. 分布式系统及其问题 zookeeper是帮助我们构建分布式系统的一个软件(协调员的角色)首先,我们要明白分布式系统以及它的问题,之后才能理解为什么有zookeeper 1.1 分布式系统 分布式 ...
- Visual Studio 2010 C++ 属性设置基础
在 <Visual Studio 2010 C++ 工程文件解读>中提到了C++工程中可以进行用户自定义的属性设置,如何进行属性设置呢? 下面我们来了解一下 props 文件的基本规则: ...
- session.资料
1. HttpSessionListener https://www.cnblogs.com/diewufeixian/p/4221747.html 2. 3. 4. 5.
- Spring自定义注解扫描的实现
目标:实现自定义spring自动扫描注解.主要为后期实现分布式服务框架自动注解提供技术支持 技术分析:通过配置组件扫描标签使spring解析标签. 1. JewelScanBeanDefaultPar ...