Object的wait、notify和notifyAll
Obect的wait、notify 和 notifyAll是Object提供的同步方法,也就是所有对象都生而带来的方法,估计搞java的没有不知道这几个方法的。那么他究竟是怎么使用的呢?在此处记录一下自己的理解。
先上一个最最最简单的例子。
public class SynchronizedTest {
public static void main(String[] args) throws Exception {
Thread mt = new Thread(){
@Override
public void run() {
synchronized (this) {
System.out.println("开始阻塞啦");
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("阻塞结束啦");
}
}
};
mt.start();
Thread.sleep(500);
synchronized (mt) {
mt.notify();
}
}
}
运行结果:
开始阻塞啦
阻塞结束啦
上面的例子中,wait和notify方法都是在synchronized代码体中执行的,如果没有经过synchronized修饰,直接使用则会抛出java.lang.IllegalMonitorStateException异常。
至于原因,jdk源码wait方法中的描述为:
* The current thread must own this object's monitor. The thread
* releases ownership of this monitor and waits until another thread
* notifies threads waiting on this object's monitor to wake up
* either through a call to the {@code notify} method or the
* {@code notifyAll} method. The thread then waits until it can
* re-obtain ownership of the monitor and resumes execution.
翻译过来:
当前线程必须拥有此对象监视器。该线程释放对此监视器的所有权并等待,直到其他线程通过调用 notify 方法,或 notifyAll 方法通知在此对象的监视器上等待的线程醒来。然后该线程将等到重新获得对监视器的所有权后才能继续执行。
总结过来就是:要想使用wait等一系列方法,必须拥有当前对象的监视器(很多地方称为监视器锁)。
那么什么是对象的监视器呢?
简单说监视器是java对象为实现同步操作的一种机制,使用javap查看上边例子的线程部分的反编译指令:
final class com.xxx.SynchronizedTest$1 extends java.lang.Thread {
com.xxx.SynchronizedTest$1();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Thread."<init>":()V
4: return
public void run();
Code:
0: aload_0
1: dup
2: astore_1
3: monitorenter
4: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
7: ldc #3 // String 开始阻塞啦
9: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
12: aload_0
13: invokevirtual #5 // Method java/lang/Object.wait:()V
16: goto 24
19: astore_2
20: aload_2
21: invokevirtual #7 // Method java/lang/InterruptedException.printStackTrace:()V
24: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
27: ldc #8 // String 阻塞结束啦
29: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
32: aload_1
33: monitorexit
34: goto 42
37: astore_3
38: aload_1
39: monitorexit
40: aload_3
41: athrow
42: return
Exception table:
from to target type
12 16 19 Class java/lang/InterruptedException
4 34 37 any
37 40 37 any
}
13行和27行可以看到monitorenter和monitorexit两条指令,monitorenter是尝试获取monitor,如果成功则执行,不成功则阻塞等待。monitorexit是释放monitor。
那么如何拥有该对象的监视器呢?
jdk源码notify方法中列举了三种方法:
By executing a synchronized instance method of that object.
By executing the body of a {@code synchronized} statement
that synchronizes on the object.
For objects of type {@code Class,} by executing a
synchronized static method of that class.
翻译大概是:
通过执行此对象的同步实例方法。
通过执行在此对象上进行同步的 synchronized 语句的代码块。
对于 Class 类型的对象,可以通过执行该类的同步静态方法。
我理解的就是被synchronized 修饰的方法或代码块(如果是代码块,需要使用同一对象做为synchronized的参数,目的是为了获取相同的监视器)。结合上边反编译的线程匿名内部类指令可以看到,使用synchronized 修饰的代码会使用monitorenter指令获取监视器,wait和notify必须获得监视器才能正确执行。
下面列举一下针对文章开始实例的错误示范:
错误实例1
public class SynchronizedTest {
public static void main(String[] args) throws Exception {
Thread mt = new Thread(){
@Override
public void run() {
synchronized (this) {
System.out.println("开始阻塞啦");
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("阻塞结束啦");
}
}
};
mt.start();
Thread.sleep(500);
// synchronized (mt) {
mt.notify();
// }
}
}
执行结果:
开始阻塞啦
Exception in thread "main" java.lang.IllegalMonitorStateException
at java.lang.Object.notify(Native Method)
at xxx
原因:
notify方法没有获得监视器。
错误实例2:
public class SynchronizedTest {
public static void main(String[] args) throws Exception {
Thread mt = new Thread(new Runnable() {
@Override
public void run() {
synchronized (this) {
System.out.println("开始阻塞啦");
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("阻塞结束啦");
}
}
});
mt.start();
Thread.sleep(500);
synchronized (mt) {
mt.notify();
}
}
}
执行结果:
开始阻塞啦
(线程持续wait中。。。
原因:
这个例子是我最开始的写法,放在这里有点不太合适,因为他并不是wait和notify错误使用导致的问题,而是错误使用Runnable导致的,最终我还是决定放上来吧,防止有人也会一时想不明白。
例子中使用 new Runnable 创建了一个匿名内部类并作为构造参数传给new Thread,导致构造的对象mt和匿名内部类的this不是同一个对象。所以导致notify不起作用= =、
Object的wait、notify和notifyAll的更多相关文章
- 使用Object的wait,notify,notifyAll做线程调度
我们知道java中的所有类的祖先都是Object,Object类有四个个方法wait(),wait(long timeout),notify(),notifyAll(),这四个方法可以用来做线程的调度 ...
- java锁与监视器概念 为什么wait、notify、notifyAll定义在Object中 多线程中篇(九)
在Java中,与线程通信相关的几个方法,是定义在Object中的,大家都知道Object是Java中所有类的超类 在Java中,所有的类都是Object,借助于一个统一的形式Object,显然在有些处 ...
- object的wait()、notify()、notifyAll()、方法和Condition的await()、signal()方法
wait().notify()和notifyAll()是 Object类 中的方法 从这三个方法的文字描述可以知道以下几点信息: 1)wait().notify()和notifyAll()方法是本地方 ...
- Object的wait/notify/notifyAll&&Thread的sleep/yield/join/holdsLock
一.wait/notify/notifyAll都是Object类的实例方法 1.wait方法:阻塞当前线程等待notify/notifyAll方法的唤醒,或等待超时后自动唤醒. wait等待其实是对象 ...
- Java-JUC(九):使用Lock替换synchronized,使用Condition的await,singal,singalall替换object的wait,notify,notifyall实现线程间的通信
Condition: condition接口描述了可能会与锁有关的条件变量.这些用法上与使用object.wait访问隐式监视器类似,但提供了更强大的功能.需要特别指出的是,单个lock可能与多个Co ...
- Thread之七:Object里的wait、notify、notifyAll的使用方法
wait().notify().notifyAll()是三个定义在Object类里的方法,可以用来控制线程的状态 public final native void notify(); public f ...
- 如何在 Java 中正确使用 wait, notify 和 notifyAll(转)
wait, notify 和 notifyAll,这些在多线程中被经常用到的保留关键字,在实际开发的时候很多时候却并没有被大家重视.本文对这些关键字的使用进行了描述. 在 Java 中可以用 wait ...
- 线程同步以及 yield() wait()和notify()、notifyAll()
1.yield() 该方法与sleep()类似,只是不能由用户指定暂停多长时间,并且yield()方法只能让同优先级的线程有执行的机会. 2.wait()和notify().notifyAll() 这 ...
- Java Thread wait, notify and notifyAll Example
Java Thread wait, notify and notifyAll Example Java线程中的使用的wait,notify和nitifyAll方法示例. The Object clas ...
随机推荐
- .Net Core 三大Redis客户端对比和使用心得
前言 稍微复杂一点的互联网项目,技术选型都可能会涉及Redis,.NetCore的生态越发完善,支持.NetCore的Redis客户端越来越多, 下面三款常见的Redis客户端,相信大家平时或多或少用 ...
- 体验RxJava
RxJava是 ReactiveX在 Java上的开源的实现,简单概括,它就是一个实现异步操作的库,使用时最直观的感受就是在使用一个观察者模式的框架来完成我们的业务需求: 其实java已经有了现成的观 ...
- YARN底层基础库
YARN基础库是其他一切模块的基础,它的设计直接决定了YARN的稳定性和扩展性,YARN借用了MRV1的一些底层基础库,比如RPC库等,但因为引入了很多新的软件设计方式,所以它的基础库更多,包括直 ...
- MySQL数据库笔记六:数据定义语言及数据库的备份和修复
1. MySQL中的函数 <1>加密函数 password(str) 该函数可以对字符串str进行加密,一般情况下,此函数给用户密码加密. select PASSWORD('tlxy666 ...
- effective java 3th item1:考虑静态工厂方法代替构造器
传统的方式获取一个类的实例,是通过提供一个 public 构造器.这里有技巧,每一个程序员应该记住.一个类可以对外提供一个 public 的 静态工厂方法 ,该方法只是一个朴素的静态方法,不需要有太多 ...
- vue实现对语言的切换,结合vue-il8n。
1.安装vue-i18n: npm install vue-i18n 如果npm长时间无反应,或安装失败,可以换成淘宝镜像安装: cnpm install vue-i18n 2.在main.js中引用 ...
- 一位996、CRUD开发者的一天
记一笔流水账 今天我打算记一笔流水账,主要记录我的一天中干的事情,并思考效率低下的原因,同时分析一些可用的解决方案. 清早·开始做计划 早上六点四十,被梦想唤醒,然后看一会书,吃早餐,送娃上学. 九点 ...
- [python]文档字符串
文档字符串可以在运行时访问,也可以用来自动生成文档. 输入: def foo(): print "This is a doc string" return True foo() 运 ...
- CodeForces 939E Maximize
Maximize 题意:整个程序有2种操作,操作1将一个元素放入集合S中,且保证最新插入的元素不小于上一次的元素, 操作2 找到集合S中的某个子集合, 使得 集合中最大的元素减去平均数的值最大. 题解 ...
- codeforces 869 E. The Untended Antiquity(树状数组)
题目链接:http://codeforces.com/contest/869/problem/E 题解:这题是挺好想到solution的但是不太好写,由于题目的特殊要求每个矩形不会重贴所以只要这两个点 ...