线程中wait/notify/notifyAll的用法
前言
多线程时,最关注的就是线程同步,线程间的同步一般用锁来实现,常见的锁就是synchronized和lock。用了synchronized,就不得不提到wait/notify/notifyAll。本文介绍这三者是什么东西。
举例说明
首先明确一点,所有的锁都是加在对象上面的。也就是说,只要是加了同步synchronized的代码,每个线程在运行到这的时候,都要去查一下这个对象上的锁有没有被人占用,如果被人占用了,就要等待。等待也分两种,一种是一直等着,一种是先做别的,别人提醒之后再找。
举个生活中的例子做对比。假设有一个屋子,屋子有一个门,门上面有一个锁。屋子同一个时间只能有一个人,这个人在里面干什么没人管。现在有多个人想进到这个屋子里面,每个进去的人都可以锁门。synchronized(object)里面的object就是门上这把锁,synchronized代码的意思是在执行代码之前,看看这把锁是不是锁着的,如果不是锁着的,就进去工作,同时把门锁上。执行完{}代码之后,就相当于从屋子里面出来,同时打开了锁。wait的意思就某个人甲进入了屋子,工作了一会,想临时先出去。所以他把锁打开,出去了,并把屋子让给了别人。那他什么时候回来呢?必须有别人告诉他,也就是notify。notify和notifyAll的区别在于notify随机告诉某一个人(注意这个人是wait的),我不用锁了,你用吧;notifyAll是告诉所有等待这个锁的人。
我们下面用这个例子加代码说明这些用法。
情况1:不锁门,不加锁
如果不锁门,就会有多个人进入到这个屋子,这个屋子就是被撑爆。也就是说,如果代码里面不加synchronize,会有多个进程访问同一个资源,造成不可知的错误。这种情况就不举代码的例子了。
情况2:锁门,不加wait和notify
这种情况下,代码如WaitNotifyDemo3所示:
package com.wardensky.multithread.waitnotify;
/**
* 这个例子就是加锁了,但没有加wait和notify
*
* @author zch
*
*/
public class WaitNotifyDemo3 {
/**
* 这个对象就是所有线程都用的锁。
*/
public static Object obj = new Object();
public static void main(String[] args) throws InterruptedException {
Thread0_1 t1 = new Thread0_1("Thread WaterMelon");
Thread0_2 t2 = new Thread0_2("Thread Apple");
t2.start();
Thread.sleep(1);
t1.start();
System.out.println("完成");
}
}
/**
* 这个线程用来演示阻塞,等这个线程工作完,不会告诉别人。<br/>
*
* @author zch <br/>
*
*/
class Thread3_1 extends Thread {
String name;
public Thread3_1(String name) {
this.name = name;
}
@Override
public void run() {
try {
synchronized (WaitNotifyDemo3.obj) {
for (int i = 1; i < 5; i++) {
Thread.sleep(1000);
System.out.println("I'm " + this.name + ". I'm doing something " + i);
}
}
} catch (InterruptedException e1) {
e1.printStackTrace();
}
}
}
/**
* 这个类用来表示被阻塞的线程,但这个线程并没有wait,所以他一直轮询这个锁,比较占资源。<br/>
*
* @author zch
*
*/
class Thread3_2 extends Thread {
String name;
public Thread3_2(String name) {
this.name = name;
}
@Override
public void run() {
System.out.println("I'm " + this.name + " . I'm waiting");
synchronized (WaitNotifyDemo3.obj) {
System.out.println(this.name + " Finish wait");
}
}
}
输出如下:
I'm Thread Apple . I'm waiting
完成
I'm Thread WaterMelon. I'm doing something 1
I'm Thread WaterMelon. I'm doing something 2
I'm Thread WaterMelon. I'm doing something 3
I'm Thread WaterMelon. I'm doing something 4
I'm Thread WaterMelon. The object notify
Thread Apple Finish wait
在上面这个例子里面,Apple线程一直在轮询这个锁的状态,等到线程WaterMelon用完之后,马上抢占过来。这种情况下,比较耗资源。
情况3: 加wait和notify
我们在情况2的基础上,让Apple线程增加一个wait,让线程WaterMelon增加一个notify,代码如下:
package com.wardensky.multithread.waitnotify;
public class WaitNotifyDemo0 {
/**
* 这个对象就是所有线程都用的锁。
*/
public static Object obj = new Object();
public static void main(String[] args) throws InterruptedException {
Thread0_1 t1 = new Thread0_1("Thread WaterMelon");
Thread0_2 t2 = new Thread0_2("Thread Apple");
t2.start();
///这个sleep很重要。
Thread.sleep(1);
t1.start();
System.out.println("完成");
}
}
/**
* 这个线程用来演示阻塞,等这个线程工作完,会通过notify方法告诉别人。<br/>
*
* @author zch <br/>
*
*/
class Thread0_1 extends Thread {
String name;
public Thread0_1(String name) {
this.name = name;
}
@Override
public void run() {
try {
synchronized (WaitNotifyDemo0.obj) {
for (int i = 1; i < 5; i++) {
Thread.sleep(1000);
System.out.println("I'm " + this.name + ". I'm doing something " + i);
}
WaitNotifyDemo0.obj.notify();
System.out.println("I'm " + this.name + ". The object notify");
}
} catch (InterruptedException e1) {
e1.printStackTrace();
}
}
}
/**
* 这个类用来表示被阻塞的线程,进入工作状态后,他可以先把锁还给别人,等别人通知他,他才继续工作。<br/>
*
* @author zch
*
*/
class Thread0_2 extends Thread {
String name;
public Thread0_2(String name) {
this.name = name;
}
@Override
public void run() {
try {
System.out.println("I'm " + this.name + " . I'm waiting");
synchronized (WaitNotifyDemo0.obj) {
WaitNotifyDemo0.obj.wait();
System.out.println(this.name + " Finish wait");
}
} catch (InterruptedException e1) {
e1.printStackTrace();
}
}
}
输出:
I'm Thread Apple . I'm waiting
完成
I'm Thread WaterMelon. I'm doing something 1
I'm Thread WaterMelon. I'm doing something 2
I'm Thread WaterMelon. I'm doing something 3
I'm Thread WaterMelon. I'm doing something 4
I'm Thread WaterMelon. The object notify
Thread Apple Finish wait
在这个例子里面,Thread0_2这个线程在synchronized (WaitNotifyDemo0.obj)
之后调用了WaitNotifyDemo0.obj.wait();
,意思是 我进入了屋子之后,先离开了,让别人处理,但别人处理之后要记得notify我,不然我就不知道了。
需要注意的是,在这个例子里面,两个线程的启动之间有一个sleep,这个sleep很重要,下面会讲到。
情况4:有多个人wait
如果有多个人wait,则唤醒线程尽量用notifyAll。notify是告诉某一个线程,但具体是哪个线程是随机的。notifyAll就是大家再来竞争。
如果用了没有用notifyAll,也可以wait执行之后调用notify,也就是一个执行完再唤醒另外一个。代码示例如下:
package com.wardensky.multithread.waitnotify;
public class WaitNotifyDemo1 {
public static Object obj = new Object();
public static void main(String[] args) {
Thread1_1 t1 = new Thread1_1();
Thread1_2 t2 = new Thread1_2("Thread Apple");
Thread1_2 t3 = new Thread1_2("Thread Orange");
t2.start();
t3.start();
try {
// 必须sleep一下
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
t1.start();
System.out.println("完成");
}
}
class Thread1_1 extends Thread {
@Override
public void run() {
try {
synchronized (WaitNotifyDemo1.obj) {
for (int i = 1; i < 5; i++) {
Thread.sleep(1000);
System.out.println("I'm thread111. I'm doing something " + i);
}
// WaitNotifyDemo.obj.notify();
WaitNotifyDemo1.obj.notifyAll();
System.out.println("I'm thread111. The object notify");
}
} catch (InterruptedException e1) {
e1.printStackTrace();
}
}
}
class Thread1_2 extends Thread {
String name;
public Thread1_2(String name) {
this.name = name;
}
@Override
public void run() {
try {
System.out.println("I'm " + this.name + " . I'm waiting");
synchronized (WaitNotifyDemo1.obj) {
WaitNotifyDemo1.obj.wait();
System.out.println(this.name + " Finish wait");
// 如果不加这句话,就叫不醒别人了。
// WaitNotifyDemo.obj.notify();
}
} catch (InterruptedException e1) {
e1.printStackTrace();
}
}
}
输出:
I'm Thread Apple . I'm waiting
I'm Thread Orange . I'm waiting
完成
I'm thread111. I'm doing something 1
I'm thread111. I'm doing something 2
I'm thread111. I'm doing something 3
I'm thread111. I'm doing something 4
I'm thread111. The object notify
Thread Orange Finish wait
Thread Apple Finish wait
情况5:情况2里面的不加sleep
如果情况2里面的例子不加sleep,会出现程序无法执行完的情况。原因是两个线程几乎同时运行,但线程2在同步代码之前有一个输出System.out.println("I'm " + this.name + " . I'm waiting");
。这个输出会导致他比线程1晚进入同步代码。线程1notify的时候,线程2还没有等待。等到线程2真正等待的时候,已经没有人来唤醒他了。
再看看源码
前面提到过,wait/notify/notifyAll都是在对象上的。实际上,Object对象上都有这些方法。代码如下:
public final native void wait(long timeout) throws InterruptedException;
public final void wait(long timeout, int nanos) throws InterruptedException {
if (timeout < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (nanos < 0 || nanos > 999999) {
throw new IllegalArgumentException(
"nanosecond timeout value out of range");
}
if (nanos > 0) {
timeout++;
}
wait(timeout);
}
public final void wait() throws InterruptedException {
wait(0);
}
public final native void notify();
public final native void notifyAll();
从源码中可以看出notify();
和notifyAll();
都是native的方法。
wait还有几个重载,可以指定等多久。比如下面的代码:
@Override
public void run() {
System.out.println("I'm " + this.name + " . I'm waiting");
try {
synchronized (WaitNotifyDemo5.obj) {
WaitNotifyDemo5.obj.wait(1000 * 1);
System.out.println(this.name + " Finish wait");
}
} catch (Exception e1) {
e1.printStackTrace();
}
}
这个代码的意思是我等1秒钟,1秒之后就进入抢占式了。当前,1秒之前也可以用notify或者notifyAll叫醒我。
跟Thread.sleep的关系
线程还有一个sleep静态方法,有人会混淆sleep和wait的关系。
还用上面的例子,sleep的意思是这个人还在屋子里面睡觉,门还锁着呢,谁也进不来。wait的意思是我把门打开,出去睡觉,等着别人把我叫醒我再回来。
也就是说,wait会释放锁,sleep不会。wait是锁这个对象的方法,sleep是线程的静态方法。
synchronized
synchronized(obj)一个代码段,意思是下面{}
里面的代码执行的时候,要去检查obj上面的锁有没有被别人占用。
同时还有这样一种写法,这是StringBuffer里面的源码:
@Override
public synchronized StringBuffer append(Object obj) {
toStringCache = null;
super.append(String.valueOf(obj));
return this;
}
这段代码相当于
@Override
public StringBuffer append(Object obj) {
synchronized(this) {
toStringCache = null;
super.append(String.valueOf(obj));
return this;
}
}
也就是说加锁的对象是this。
如果是静态方法,那么加锁的对象是obj.class,该类的类对象。
总结
- synchronized是要检查锁的代码,如果有锁不往下执行。
- wait只能在同步(synchronize)环境中被调用
- 进入wait状态的线程能够被notify和notifyAll线程唤醒
- wait通常有条件地执行,线程会一直处于wait状态,直到某个条件变为真
- wait方法在进入wait状态的时候会释放对象的锁
- wait方法是针对一个被同步代码块加锁的对象
- sleep方法和多线程没有直接关系,跟锁也没有关系
- wait释放锁,notify和notifyAll不释放锁
线程中wait/notify/notifyAll的用法的更多相关文章
- Java多线程--wait(),notify(),notifyAll()的用法
忙等待没有对运行等待线程的 CPU 进行有效的利用(而且忙等待消耗cpu过于恐怖,请慎用),除非平均等待时间非常短.否则,让等待线程进入睡眠或者非运行状态更为明智,直到它接收到它等待的信号. Java ...
- Java 多线程 线程的五种状态,线程 Sleep, Wait, notify, notifyAll
一.先来看看Thread类里面都有哪几种状态,在Thread.class中可以找到这个枚举,它定义了线程的相关状态: public enum State { NEW, RUNNABLE, BLOCKE ...
- 零基础学习java------day18------properties集合,多线程(线程和进程,多线程的实现,线程中的方法,线程的声明周期,线程安全问题,wait/notify.notifyAll,死锁,线程池),
1.Properties集合 1.1 概述: Properties类表示了一个持久的属性集.Properties可保存在流中或从流中加载.属性列表中每个键及其对应值都是一个字符串 一个属性列表可包含另 ...
- 转:【Java并发编程】之十:使用wait/notify/notifyAll实现线程间通信的几点重要说明
转载请注明出处:http://blog.csdn.net/ns_code/article/details/17225469 在Java中,可以通过配合调用Object对象的wait()方法和no ...
- 【Java并发编程】:使用wait/notify/notifyAll实现线程间通信
在java中,可以通过配合调用Object对象的wait()方法和notify()方法或notifyAll()方法来实现线程间的通信.在线程中调用wait()方法,将阻塞等待其他线程的通知(其他线程调 ...
- 【Java并发编程】之十:使用wait/notify/notifyAll实现线程间通信的几点重要说明
在Java中,可以通过配合调用Object对象的wait()方法和notify()方法或notifyAll()方法来实现线程间的通信.在线程中调用wait()方法,将阻塞等待其他线程的通知(其他线程调 ...
- 使用wait/notify/notifyAll实现线程间通信的几点重要说明
在Java中,可以通过配合调用Object对象的wait()方法和notify()方法或notifyAll()方法来实现线程间的通信.在线程中调用wait()方法,将阻塞等待其他线程的通知(其他线程调 ...
- 进程间协作---wait,notify,notifyAll
转自牛客网的一篇评论,解释的十分详细 在 Java 中,可以通过配合调用 Object 对象的 wait() 方法和 notify()方法或 notifyAll() 方法来实现线程间的通信.在线程中调 ...
- 多线程-wait/notify/notifyAll
引言 在Java中,可以通过配合调用Object对象的wait,notify和notifyAll来实现线程间的通信. 在线程中调用wait方法,将阻塞带带其他线程的通知(其他线程调用notify或no ...
随机推荐
- java微信开发之接受消息回复图片或者文本
上回说到 接口连接成功,接下来是真正的开发了. 消息的接收,整个过程就是关注订阅号的用户在微信订阅号中发送消息,微信服务器接收到消息,将消息发给开发者的服务器,服务器接收消息然后可以根据内容进行回复. ...
- python learning Process and Thread.py
# 多进程 # Windows下面没有fork ,请在linux下跑下面的代码 import os print('Process (%s) start...' % os.getpid()) pid = ...
- Robot Framework 教程 (5) - 连接Oracel数据库
Robot Framework 提供了多种Library.其中Database Library可用来连接操作数据库. 1.安装Database Library 打开Robot Framework官网, ...
- scrapy-scrapy如何打开页面?[转]
一.首先我们来看scrapy spider如何打开页面: 要打开页面,我们用的是手,同样scrapy也有一个得力助手:spider:至于spider如何打开页面,且听我娓娓道来,如果一上来就噼里啪啦的 ...
- 微信小程序 功能函数 点击传参和页面
// 商品详情页跳转函数 detailInto: function (e) { // console.log() var change = e.currentTarget.dataset.id; wx ...
- yiled(),wait(),sleep()方法区别
yiled():让步 wait():等待 sleep():休眠 yiled是让步,会使当前线程由运行状态进入到就绪状态,让其他优先级高线程先执行,但是如果是同一优先级的线程,那么谁先执行就不确定了.它 ...
- Java 软引用和弱引用
软引用(SoftReference) 软引用是用来描述一些有用但并不是必需的对象,在Java中用java.lang.ref.SoftReference类来表示.对于软引用关联着的对象,只有在内存不足的 ...
- 【转载】css3动画简介以及动画库animate.css的使用
原文地址:http://www.cnblogs.com/2050/p/3409129.html 在这个年代,你要是不懂一点点css3的知识,你都不好意思说你是个美工.美你妹啊,请叫我前端工程师好不好. ...
- matlab gradient 和 prctile
介绍两个matlab小函数: 1.gradient 借用别人的例子:例:>> x=[6,9,3,4,0;5,4,1,2,5;6,7,7,8,0;7,8,9,10,0]x = 6 ...
- DAY...
讲道理,我还是以前的我.没有坚持每天写......... 又到了谷底......... 坚持啊........