Java多线程 wait, notify 和 notifyAll
Java的Object类
public class Object {
public final native void notify(); public final native void notifyAll(); public final native void wait(long timeout) throws InterruptedException;
}
调用这些方法的当前线程必须拥有此对象监视器,否则将会报java.lang.IllegalMonitorStateException exception
wait;
Object的wait方法有三个重载方法,其中一个方法wait() 是无限期(一直)等待,直到其它线程调用notify或notifyAll方法唤醒当前的线程;另外两个方法wait(long timeout) 和wait(long timeout, int nanos)允许传入 当前线程在被唤醒之前需要等待的时间,timeout为毫秒数,nanos为纳秒数。
wait():释放占有的对象锁,线程进入等待池,释放cpu,而其他正在等待的线程即可抢占此锁,获得锁的线程即可运行程序。
而sleep()不同的是,线程调用此方法后,会休眠一段时间,休眠期间,会暂时释放cpu,但并不释放对象锁。也就是说,在休眠期间,其他线程依然无法进入此代码内部。休眠结束,线程重新获得cpu,执行代码。
wait()和sleep()最大的不同在于wait()会释放对象锁,而sleep()不会!
notify:
notify方法只唤醒一个等待(对象的)线程并使该线程开始执行。
调用notify()后,并不会立即释放锁,而是继续执行当前代码,直到synchronized中的代码全部执行完毕,才会释放对象锁。JVM则会在等待的线程中调度一个线程去获得对象锁,执行代码。
需要注意的是,wait()和notify()必须在synchronized代码块中调用。
所以如果有多个线程等待一个对象,这个方法只会唤醒其中一个线程,选择哪个线程取决于操作系统对多线程管理的实现。
notifyAll:
notifyAll 会唤醒所有等待(对象的)线程,尽管哪一个线程将会第一个处理取决于操作系统的实现。
这些方法可以使用于“生产者-消费者”问题,消费者是在队列中等待对象的线程,生产者是在队列中释放对象并通知其他线程的线程。
demo:
public class Main {
public static void main(String[] args) {
Message msg = new Message("process it");
Waiter waiter = new Waiter(msg);
new Thread(waiter,"waiterThread").start(); Waiter waiter1 = new Waiter(msg);
new Thread(waiter1, "waiter1Thread").start(); Notifier notifier = new Notifier(msg);
new Thread(notifier, "notifierThread").start();
System.out.println("All the threads are started");
}
}
class Message {
private String msg;
public Message(String str){
this.msg=str;
}
public String getMsg() {
return msg;
}
public void setMsg(String str) {
this.msg=str;
}
} class Waiter implements Runnable{
private Message msg;
public Waiter(Message m){
this.msg=m;
} @Override
public void run() {
String name = Thread.currentThread().getName();
synchronized (msg) {
try{
System.out.println(name+" waiting to get notified at time:"+System.currentTimeMillis());
msg.wait();
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println(name+" waiter thread got notified at time:"+System.currentTimeMillis());
//process the message now
System.out.println(name+" processed: "+msg.getMsg());
}
}
} class Notifier implements Runnable {
private Message msg; public Notifier(Message msg) {
this.msg = msg;
} @Override
public void run() {
String name = Thread.currentThread().getName();
System.out.println(name+" started");
try {
Thread.sleep(1000);
synchronized (msg) {
msg.setMsg(name+" Notifier work done");
msg.notify();
// msg.notifyAll();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
All the threads are started
waiter1Thread waiting to get notified at time:1480592377675
waiterThread waiting to get notified at time:1480592377677
notifierThread started
waiter1Thread waiter thread got notified at time:1480592378685
waiter1Thread processed: notifierThread Notifier work done
Notifier中的run方法改成msg.notifyAll();
Result:
All the threads are started
waiter1Thread waiting to get notified at time:1480592533780
waiterThread waiting to get notified at time:1480592533780
notifierThread started
waiterThread waiter thread got notified at time:1480592534782
waiterThread processed: notifierThread Notifier work done
waiter1Thread waiter thread got notified at time:1480592534782
waiter1Thread processed: notifierThread Notifier work done
一个通知线程,3个等待线程:
public class Main {
private String flag[] = { "true" };
public static void main(String[] args) {
System.out.println("Main Thread Run!");
Main test = new Main();
System.nanoTime();
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();
} 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() {
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!");
}
}
}
}
Main Thread Run!
waiter01 begin waiting!
waiter02 begin waiting!
waiter03 begin waiting!
wait time :2999
waiter03 end waiting!
wait time :2999
waiter02 end waiting!
wait time :2999
waiter01 end waiting!
=====================================================================================
wait和sleep的demo:
public class Main {
public static void main(String[] args) {
new Thread(new Thread1()).start();
try {
Thread.sleep(5000);
} catch (Exception e) {
e.printStackTrace();
}
new Thread(new Thread2()).start();
} private static class Thread1 implements Runnable{
@Override
public void run(){
synchronized (Main.class) {
System.out.println("enter thread1...");
System.out.println("thread1 is waiting...");
try {
//调用wait()方法,线程会放弃对象锁,进入等待此对象的等待锁定池
Main.class.wait();
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("thread1 is going on ....");
System.out.println("thread1 is over!!!");
}
}
}
private static class Thread2 implements Runnable{
@Override
public void run(){
synchronized (Main.class) {
System.out.println("enter thread2....");
System.out.println("thread2 is sleep....");
//只有针对此对象调用notify()方法后本线程才进入对象锁定池准备获取对象锁进入运行状态。
Main.class.notify();
//==================
//区别
//如果我们把代码:Main.class.notify();给注释掉,即Main.class调用了wait()方法,但是没有调用notify()
//方法,则线程永远处于挂起状态。
try {
//sleep()方法导致了程序暂停执行指定的时间,让出cpu该其他线程,
//但是他的监控状态依然保持者,当指定的时间到了又会自动恢复运行状态。
//在调用sleep()方法的过程中,线程不会释放对象锁。
Thread.sleep(5000);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("thread2 is going on....");
System.out.println("thread2 is over!!!");
}
}
}
}
enter thread1...
thread1 is waiting...
enter thread2....
thread2 is sleep....
thread2 is going on....
thread2 is over!!!
thread1 is going on ....
thread1 is over!!!
wait,notify的使用,线程同步唤醒
package com.qhong; /**
* Created by Administrator on 2017/7/4 0004.
*/
public class MyThreadPrinter2 implements Runnable { private String name;
private Object prev;
private Object self; private MyThreadPrinter2(String name, Object prev, Object self) {
this.name = name;
this.prev = prev;
this.self = self;
} @Override
public void run() {
int count = ;
while (count > ) {
synchronized (prev) {
synchronized (self) {
System.out.print(name);
count--; self.notify();
}
try {
prev.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
} }
} public static void main(String[] args) throws Exception {
Object a = new Object();
Object b = new Object();
Object c = new Object();
MyThreadPrinter2 pa = new MyThreadPrinter2("A", c, a);
MyThreadPrinter2 pb = new MyThreadPrinter2("B", a, b);
MyThreadPrinter2 pc = new MyThreadPrinter2("C", b, c); new Thread(pa).start();
Thread.sleep(); //确保按顺序A、B、C执行
new Thread(pb).start();
Thread.sleep();
new Thread(pc).start();
Thread.sleep();
}
}
Output:
ABCABCABCABCABCABCABCABCABCABC
Thread.sleep(100)是保证ABC的执行顺序
在这个例子中,可以深刻的体现出wait不仅是阻塞线程,更体现wait释放锁对象。
比如是4轮,第一轮的abc是有Thread.sleep来控制的,这个时候只有锁对象c是被释放的,锁对象a阻塞线程B,锁对象b阻塞线程C
第二轮,a的prev是c,所以是可以运行的,并且在这一步释放锁对象a,锁对象c注释线程A
................
就这样一轮一轮的控制,虽然想通了,但是让我写还是想不出来的,挺繁琐!
有是一个demo
package com.qhong.thread; public class MyWaitNotify{ MonitorObject myMonitorObject = new MonitorObject();
boolean wasSignalled = false; public void doWait(){
synchronized(myMonitorObject){
while(!wasSignalled){
try{
myMonitorObject.wait();
} catch(InterruptedException e){...}
}
//clear signal and continue running.
wasSignalled = false;
}
} public void doNotify(){
synchronized(myMonitorObject){
wasSignalled = true;
myMonitorObject.notify();
}
}
} class MonitorObject{}
代码比较简单,但是要注意下面的说明,有好几个概念以前没注意:
1、不管是等待线程还是唤醒线程都在同步块里调用wait()和notify()。这是强制性的!一个线程如果没有持有对象锁,将不能调用wait(),notify()或者notifyAll()。否则,会抛出IllegalMonitorStateException异常。
2、一旦线程调用了wait()方法,它就释放了所持有的监视器对象上的锁。这将允许其他线程也可以调用wait()或者notify()。
3、为了避免丢失信号,必须把它们保存在信号类里。如上面的wasSignalled变量。
4、假唤醒:由于莫名其妙的原因,线程有可能在没有调用过notify()和notifyAll()的情况下醒来。这就是所谓的假唤醒(spurious wakeups)。为了防止假唤醒,保存信号的成员变量将在一个while循环里接受检查,而不是在if表达式里。这样的一个while循环叫做自旋锁。
5、不要在字符串常量或全局对象中调用wait()。即上面MonitorObject不能是字符串常量或是全局对象。每一个MyWaitNotify的实例都拥有一个属于自己的监视器对象,而不是在空字符串上调用wait()/notify()。
http://www.cnphp6.com/archives/62258
http://blog.csdn.net/luoweifu/article/details/46664809
http://developer.51cto.com/art/201508/487488.htm
http://blog.csdn.net/a352193394/article/details/39381271
http://blog.csdn.net/evankaka/article/details/44153709
Java多线程 wait, notify 和 notifyAll的更多相关文章
- Java Thread wait, notify and notifyAll Example
Java Thread wait, notify and notifyAll Example Java线程中的使用的wait,notify和nitifyAll方法示例. The Object clas ...
- [译]Java Thread wait, notify和notifyAll示例
Java Thread wait, notify和notifyAll示例 Java上的Object类定义了三个final方法用于不同线程间关于某资源上的锁状态交互,这三个方法是:wait(), not ...
- Java多线程--wait(),notify(),notifyAll()的用法
忙等待没有对运行等待线程的 CPU 进行有效的利用(而且忙等待消耗cpu过于恐怖,请慎用),除非平均等待时间非常短.否则,让等待线程进入睡眠或者非运行状态更为明智,直到它接收到它等待的信号. Java ...
- Java的wait(), notify()和notifyAll()使用小结
wait(),notify()和notifyAll()都是java.lang.Object的方法: wait(): Causes the current thread to wait until an ...
- Java的wait(), notify()和notifyAll()使用心得(转)
本篇文章是对java的 wait(),notify(),notifyAll()进行了详细的分析介绍,需要的朋友参考下wait(),notify()和notifyAll()都是java.lang.Obj ...
- java wait()和notify()、notifyAll()
图见<JAVA并发编程的艺术>P98-101 这三个方法都是java.lang.Object的方法,用于协调多个线程对共享数据的存取,必须在synchronized语句块中使用!这三个方法 ...
- java中的notify和notifyAll有什么区别?
先说两个概念:锁池和等待池 锁池:假设线程A已经拥有了某个对象(注意:不是类)的锁,而其它的线程想要调用这个对象的某个synchronized方法(或者synchronized块),由于这些线程在进入 ...
- 精通java并发-wait,notify和notifyAll的总结(含案例)
目前CSDN,博客园,简书同步发表中,更多精彩欢迎访问我的gitee pages wait,notify和notifyAll 总结 在调用wait方法时,线程必须要持有被调用对象的锁,当调用wait方 ...
- Java多线程_wait/notify/notifyAll方法
关于这三个方法,我们可以查询API得到下列解释: wait():导致当前的线程等待,直到其他线程调用此对象的notify( ) 方法或 notifyAll( ) 方法或者指定的事件用完 notify( ...
随机推荐
- JavaScript编码规范[百度]
JavaScript编码规范 1 前言 2 代码风格 2.1 文件 2.2 结构 2.2.1 缩进 2.2.2 空格 2.2.3 换行 2.2.4 语句 2.3 命名 ...
- velocity学习记录
一.引入文件 静态引入:#include("./footer.vm.html") 动态引入:#parse("./header.vm.html") 说明:./为v ...
- Apache Storm 衍生项目之2 -- Trident-ML
欢迎转载,转载请注明出处,徽沪一郎,谢谢. 楔子 或许谈起storm是大数据实时计算框架已经让你不明觉厉,如果说storm还可以跟机器学习算法(ml)有机的结合在一起,是不是更加觉着高大尚呢.trid ...
- 给Android程序员的六个建议
给Android程序员的六个建议 分类: 安卓相关2015-07-14 23:58 177人阅读 评论(0) 收藏 举报 android程序员 如果你一年前写的代码 , 在现在看来你还感觉写的很不错 ...
- 页面静态化2 --- 使用PHP缓存机制来完成页面静态化(上)(ob_flush和flush函数区别用法)
我们可以使用PHP自带的缓存机制来完成页面静态化,但在这里,需要说明一点,仅靠PHP缓存机制并不能完美的解决页面静态化,往往需要和其他页面静态技术(通常是伪静态技术)结合使用 例子: 当访问一个页面时 ...
- 20145317彭垚《Java程序设计》第3周学习总结
20145317彭垚<Java程序设计>第3周学习总结 教材学习内容总结 第四章 4.1类与对象 4.1.1定义类: new clothes():新建一个对象. class clothes ...
- window下mysql的1053错误
tmp文件数目过多 导致启动为1053错误 这个时候有两种方法 清空tmp目录 更改mysql的tmp目录 在my.ini中更改 [mysqld] tmpdir="d:/mysql_tmp& ...
- Memory Allocation in the MySQL Server
https://dev.mysql.com/doc/internals/en/memory-allocation-mysql-server.html MySQL Internals Manual / ...
- Java中的可变参数以及foreach语句
Java中的可变参数的定义格式如下: 返回值类型 方法名称(类型 ... 参数名称){} foreach语句的格式如下: for ( 数据类型 变量名称 :数据名称){ ... } public ...
- Oracle用户、权限、角色管理(转)
http://blog.csdn.net/junmail/article/details/4381287 Oracle 权限设置一.权限分类:系统权限:系统规定用户使用数据库的权限.(系统权限是对用户 ...