目录
一、使用Object中的方法
Object类中定义了wait/notify/notifyAll等线程间通信的方法。JDK官方文档中的解释:
22 / 100 撤消 重做标题 加粗 颜色 背景其他列表对齐 水平线 块引用 代码段 表格 图像 视频 公式 链接 模版 目录 投票 宽屏 使用 MD 编辑器
封面&摘要:单图三图无封面*
0/256
一键提取文章标签:
java开发语言 添加文章标签
*分类专栏:
新建分类专栏
文章类型:
原创转载翻译*
发布形式:全部可见仅我可见粉丝可见VIP可见
所有用户将均可访问和阅读,优质文章将会获得更高的推荐曝光
内容等级:
初级 中级 高级 *
共 4122 字发文设置
保存草稿定时发布发布博客
//唤醒在此对象监视器上等待的单个线程
public final native void notify()
//唤醒在此对象监视器上等待的所有线程
public final native void notifyAll()
//导致当前的线程等待,直到其他线程调用此对象的notify()方法或notifyAll()方法
public final void wait() throws InterruptedException
//导致当前的线程等待,直到其他线程调用此对象的notify()方法或notifyAll()方法,
//或者指定的时间过完
public final native void wait(long timeout) throws InterruptedException
//导致当前的线程等待,直到其他线程调用此对象的notify() 方法或 notifyAll() 方法,
//或者其他线程打断了当前线程,或者指定的时间过完
public final void wait(long timeout, int nanos) throws InterruptedException
用通俗一点的语言来解释wait()和notify():
wait() ------> 我等会儿再用这把锁,CPU也让给你们,我先休息一会儿!
notify() ------> 我用完了,你们谁用?
wait:会让出对象锁,同时,当前线程休眠,等待被唤醒,如果不被唤醒,就一直等在那儿。
notify:并不会让当前线程休眠,但会唤醒休眠的线程。
总结:
1、wait、notify、notifyAll都属于Object类,也就是每个对象都有wait、notify、notifyAll的功能,因为每个对象都有锁。
2、调用使用wait、notify、notifyAll的时候,一定要对竞争资源进行加锁。如果不加锁的话,则会报 IllegalMonitorStateException 异常。
3、调用wait方法后,线程状态由RUNNING变为WAITING,并将当前线程放置到对象的等待队列。
4、调用wait进行线程等待时,必须要取得这个锁,一般是放到synchronized(obj)代码中。
5、在while循环里而不是if语句下使用wait,这样,会在线程暂停恢复后都检查wait的条件,并在条件实际上并未改变的情况下处理唤醒通知。
6、notify或notifyAll方法调用后,等待线程依旧不会从wait返回,需要调用notify或notifAll的线程释放锁之后,等待线程才有机会从wait返回。
7、notify方法将等待队列中的一个等待线程从等待队列中移到同步队列中,而notifyAll方法则是将等待队列中所有的线程全部移到同步队列,被移动的线程状态由WAITING变为BLOCKED。
1.1 wait、notify、notifyAll的使用
一个线程因其执行目标动作所需的保护条件未满足而被暂停的过程称为等待,一个线程更新了系统的状态,使得其他线程所需的保护条件得以满足的时候唤醒哪些被暂停的线程的过程称为通知。
Object.wait()/Object.wait(long)以及Object.notify()/Object.notifyAll()可应用于实现等待和通知。
1.1.1 wait的作用
Object.wait()的作用是使其执行线程被暂停,直到接到通知或被中断为止,该方法可用于实现等待。
在调用 wait方法之前,线程必须要获得锁,即只能在同步方法或同步块中调用wait方法。调用wait方法之后,当前线程会释放锁。如果再次获取到锁的话,当前线程才能从wait方法处成功返回。
使用Object.wait()实现等待,示例:
//调用wait方法之前获得相应对象的内部锁
synchronized(someObject){
while(保护条件不成立){
//调用Object.wait()暂停当前线程
someObject.wait();
}
//代码执行到这里说明保护条件已满足,执行目标动作
doAction();
}
1.1.2 notify和notifyAll的作用
notify()方法也要在同步方法或同步块中调用,即在调用前,线程也必须要获得锁。
notify()方法任意从处于WAITTING状态的线程中挑选一个进行唤醒,使得调用 wait方法的线程从等待队列移入到同步队列中,从而使得调用 wait()方法的线程能够从 wait()方法处退出。
调用notify()后,当前线程不会马上释放该对象锁,要等到程序退出同步块后,当前线程才会释放锁。
notifyAll()与notify()作用类似,不同的地方:notifyAll()的作用是唤醒正在等待对象监视器的所有线程。
使用Object.notify()实现通知,示例:
synchronized(someObject){
//更新等待线程的保护条件涉及的共享变量
updateSharedState();
//唤醒其他线程
someObject.notify();
}
1.1.3 notify/notifyAll选择
Object.notify()可能导致信号丢失这样的正确性问题,而Object.notifyAll()虽然效率不高,但在正确性方面有保障。
一种较流行的保守方法:优先使用Object.notifyAll()保障正确性,只有在有证据表明使用Object.notify()足够的情况下才使用Object.notify()。
使用Object.notify()需要满足的两个条件:
一次通知仅需要唤醒最多一个线程;
相应对象的等待集中仅包含同质等待线程。(同质等待线程是指这些线程用同一个保护条件,并且这些线程在Object.wait()调用返回后的处理逻辑一致。最经典的同质线程是使用同一个Runnable接口实例创建的不同线程或者从同一个Thread子类的new出来的多个线程)。
二、案例
生产者:
public class Costs extends Thread {
// 1.需要在成员位置上创建一个包子变量
private Baozi baozi;
// 2.使用带参构造,为这个包子赋值
public Costs(Baozi baozi) {
this.baozi = baozi;
}
@Override
public void run() {
// 设置任务
// 定义一个变量
int count = 0;
while (true) {
// 必须保证两个线程只能有一个在执行
synchronized (baozi) {
// 包子状态的判断
if (baozi.flag) {
// 包子铺有包子,包子铺需要调用wait方法进入等待状态
try {
baozi.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
// 包子铺没有包子,被唤醒之后包子铺生产包子,增加难度:交替生产两种包子
if (count % 2 == 0) {
// 生产三鲜包子,薄皮
baozi.pi = "薄皮";
baozi.xian = "三鲜";
} else {
baozi.pi = "冰皮";
baozi.xian = "猪肉大葱";
}
count++;
System.out.println("包子铺正在生产:"+baozi.pi+baozi.xian+"馅的包子");
// 生产包子需要一个过程,等待3秒钟
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//包子铺生产好了包子
//修改包子状态 true
baozi.flag=true;
//唤醒吃货线程
baozi.notify();
System.out.println("包子铺已经生产好了"+baozi.pi+baozi.xian+"馅的包子,吃货可以开始吃了");
}
}
}
}
消费者:
public class Foodie extends Thread{
//1.在成员位置上定义一个包子变量
private Baozi baozi ;
//2.使用带参构造,为这个包子变量赋值
public Foodie(Baozi baozi) {
this.baozi=baozi;
}
//3.重写run方法
@Override
public void run() {
//设置线程任务,吃包子
//使用死循环,让吃货吃包子
while(true) {
//使用同步锁,保证只有一个线程在执行
synchronized (baozi) {
//对包子状态进行判断
//让吃货进入到等待状态
if(baozi.flag==false) {
try {
baozi.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//被唤醒后执行吃包子
System.out.println("吃货正在吃"+baozi.pi+baozi.xian+"馅的包子");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//吃货吃完包子,修改包子状态 为false
baozi.flag=false;
//吃货线程唤醒包子铺线程生产包子
baozi.notify();
System.out.println("吃货已经吃完了"+baozi.pi+baozi.xian+"馅的包子");
System.out.println("-*-*-*-*-*-");
}
}
}
}