consume

更新时间:2022-11-23 15:49:42 阅读: 评论:0


2022年11月23日发(作者:life in a day)

Java-wait、notify、notifyAll

关键字wait、notify、notifyAll

⼤家都知道wait、notify、notifyAll这三个是Object提供的线程间协作的⽅法,常⽤在⽣产消费者模式,那么wait跟sleep有什么区别呢?wait、

notify、notifyAll⼜该如何使⽤呢。

wait跟sleep区别

(1)共同点,wait、sleep都会让当前线程进⼊阻塞等待状态,并释放CPU时间⽚,在满⾜某个条件后被唤醒,例如timeout、中的;

(2)不同点,wait是Objec的⽅法,sleep是Thread的⽅法;

(3)异同点,wait会释放锁,⽽sleep不会释放;

wait、notify、notifyAll使⽤

场景:⽣产消费者模式

mProducerThread是⽣产者线程,负责⽣产商品,如果不能继续⽣产则wait;mConsumerThread是消费者线程,负责消费商品,如果不能继

续消费则wait。

使⽤规则:

(1)wait、notify、notifyAll必须在同步代码中执⾏;

(2)wait必须在while循环内执⾏。

publicclassGoods{

//⽣产者线程

privateThreadmProducerThread=newThread(){

@Override

publicvoidrun(){

produce();

}

};

//消费者线程

privateThreadmConsumerThread=newThread(){

@Override

publicvoidrun(){

consume();

}

};

publicvoidproduce(){

while(true){

synchronized(){

while(是否不能⽣产){//不满⾜⽣产条件

try{

();//释放锁,让出CPU

}catch(InterruptedExceptione){

tackTrace();

}

}

//⽣产商品

All();

}

}

}

publicvoidconsume(){

while(true){

synchronized(){

while(是否不能消费){//不满⾜消费条件

try{

();//释放锁,让出CPU

}catch(InterruptedExceptione){

tackTrace();

}

}

//消费商品

All();

}

}

}

//开启任务

publicvoidstart(){

();

();

}

}

以下是消费者的流程图,⽣产者也类似,

消费者.png

使⽤规则思考

wait、notify、notifyAll,为什么必须在同步代码中执⾏

(1)以上述代码为例,当不能继续⽣产或不能继续消费时,需要其他线程消费或者⽣成商品,则调⽤wait释放锁让出CPU,wait是释放锁,只有

获得锁才能释放锁,没有获得锁哪来释放锁呢,另外,线程间协作通常会共享变量,对共享变量的操作放在同步代码块中,可以解决线程安全问

题,所以wait必须在同步代码执⾏,否则会抛出lMonitorStateException:objectnotlockedbythreadbeforewait()异常;

(2)notify、notifyAll同理,⽣产完商品或消费完商品,需要唤醒其他线程并且释放锁,那么其他线程才有机会获得锁,所以在退出同步代码时

调⽤notify、notifyAll,如果不在同步代码中执⾏,则抛出lMonitorStateException:objectnotlockedbythreadbefore

notify();

(3)总结⼀点,没有获取锁,那就谈不上wait、notify、notifyAll;

notify和notifyAll区别

(1)notify,唤醒等待同⼀个对象的某个线程去竞争锁,⾄于是哪个线程获取锁,由CPU调度;

(2)notifyAll,唤醒等待同⼀个对象的所有线程去竞争锁,⾄于是哪个线程获取锁,由CPU调度,听起来跟notify⼀样;

(3)上述代码,将All()换成效果⼀样。假如,mConsumerThread消费者先获得锁进⼊同步代码,但是

发现不能继续消费商品,则wait释放锁让出CPU处于等待状态,那么mProducerThread⽣产者线程竞争获得锁进⼊同步代码⽽且可以继续⽣产

商品,则开始⽣产商品,⽣产完以后在退出同步代码之前调⽤notifyAll,那么mConsumerThread就会被唤醒去竞争锁。试想着,在等待的只

有mConsumerThread,所以⽆论是⽤notifyAll唤醒多个,还是notify唤醒某⼀个,效果都⼀样。如果是只有⼀个mConsumerThread和⼀个

mProducerThread的情况,⽤notifyAll或notify都⼀样,因为在等待的只有⼀个。

如果是多个ConsumerThread或者多个

ProducerThread的情况,⽤notifyAll显然更适合。假如,mProducerThread⽣产完商品,希望唤醒消费者来消费,那么所有消费者都应该得

到唤醒,如果使⽤notify,极端情况,只有其中某个消费者⼀直被唤醒消费,属于过得饱和,⽽其他消费者过度饥饿,显⽰不是使⽤多线程的初

衷;还有⼀种极端情况,还是只有其中某个消费者⼀直被唤醒,但是因为某个条件不满⾜,⽆法消费商品,⽽⽣产的商品已经满了,⼀直处于

wait,⽽消费者⼀直不消费商品,任务⽆法继续执⾏下去,假死现象。

(4)永远是单个线程处于等待,⽤notify或notifyAll效果都⼀样,如果是多线程处于等待,显然⽤notifyAll更合适。

wait为什么必须在while循环内执⾏

上述代码的consume⽅法,将while改成if,当mProducerThread⽣产者⽣产完商品唤醒消费者,mConsumerThread获得锁不再判断消费条

件是否成⽴,直接去消费商品,这样没有如何问题,因为很明确知道mConsumerThread被唤醒且获得锁,肯定是有商品可以消费,所以⽆需再

判断条件是否成⽴。

但如果是以下场景则不同,

mConsumerThread1与mProducerThread1,以及mConsumerThread2与mProducerThread2是成对关系。

mConsumerThread1只能消费mProducerThread1⽣产的商品,mConsumerThread2只能消费mProducerThread2⽣产的商品。

当mConsumerThread1、mConsumerThread2都不满⾜消费条件处于等待状态,这时mProducerThread1⽣产了商品,调⽤notifyAll唤

醒线程来消费,因为调⽤的是notifyAll,所以mConsumerThread1、mConsumerThread2都会被唤醒,如果正好是mConsumerThread2

获得了锁,需要重新判断条件是否满⾜,因为mConsumerThread2是跟mProducerThread2对应的,这时候while循环就起⽤了。

如果是有多组执⾏条件,唤醒存在假唤醒的情况(中断唤醒),那么⽤while循环显然更合适,这也是Java推荐的。

publicclassGoods{

//⽣成者线程1

privateThreadmProducerThread1=newThread(){

@Override

publicvoidrun(){

produce1();

}

};

//消费者线程2

privateThreadmConsumerThread1=newThread(){

@Override

publicvoidrun(){

consume1();

}

};

//⽣成者线程2

privateThreadmProducerThread2=newThread(){

@Override

publicvoidrun(){

produce2();

}

};

//消费者线程2

privateThreadmConsumerThread2=newThread(){

@Override

publicvoidrun(){

consume2();

}

};

publicvoidproduce1(){

while(true){

synchronized(){

while(是否不能⽣产){//不满⾜⽣成条件1

try{

();//释放锁,让出CPU

}catch(InterruptedExceptione){

tackTrace();

}

}

//⽣成商品

All();

}

}

}

publicvoidconsume1(){

while(true){

synchronized(){

while(是否不能消费){//不满⾜消费条件1

try{

();//释放锁,让出CPU

}catch(InterruptedExceptione){

tackTrace();

}

}

//消费商品

All();

}

}

}

publicvoidproduce2(){

while(true){

synchronized(){

while(是否不能⽣产){//不满⾜⽣产条件2

try{

();//释放锁,让出CPU

}catch(InterruptedExceptione){

tackTrace();

}

}

//⽣成商品

All();

}

}

}

publicvoidconsume2(){

while(true){

synchronized(){

while(是否不能消费商品){//不满⾜消费条件2

try{

();//释放锁,让出CPU

}catch(InterruptedExceptione){

tackTrace();

}

}

//消费商品

All();

}

}

}

//开启任务

publicvoidstart(){

();

();

();

();

}

}

总结:

(1)wait、notify、notifyAll必须在同步代码中执⾏;

(2)wait必须在while循环内执⾏;

(3)notify⽐notifyAll更容易出现死锁。

以上分析有不对的地⽅,请指出,互相学习,谢谢哦!

本文发布于:2022-11-23 15:49:42,感谢您对本站的认可!

本文链接:http://www.wtabcd.cn/fanwen/fan/90/6654.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

上一篇:phenomenon
标签:consume
相关文章
留言与评论(共有 0 条评论)
   
验证码:
Copyright ©2019-2022 Comsenz Inc.Powered by © 专利检索| 网站地图