java wait notify notifyall,wait方法和notify方法

  java wait notify notifyall,wait方法和notify方法

  00-1010 1.睡眠(长n)和等待(长n)的区别2。wait-notify方法的正确使用[while(条件)wait]2.1问题12.2问题22.3问题32.4问题42.5最终结果摘要问题:

  sleep()和wait()有什么区别?

  00-1010 (1) Sleep是线程方法,wait是对象的方法;

  (2) sleep不需要配合synchronized使用,wait需要配合synchronized使用;

  (3)睡眠时不会释放对象锁,等待时会释放对象锁;

  (4)它们的状态定时_等待;

  @ SLF 4j public class Test1 { private static final Object lock=new Object();public static void main(string[]args)引发中断的异常{ new thread(()-{ synchronized(lock){ log . debug( t1 thread gets lock . )));试试{ thread . sleep(20000);} catch(interrupted exception e){ e . printstacktrace();} } }, t1 )。start();//等待1s,让线程执行t1线程thread . sleep(1000);//无法获取锁,因为t1线程没有释放锁synchronized (lock){ log.debug(主线程想要获取锁. ));}}}执行sleep()方法后的结果:线程t1睡眠期间,主线程没有得到锁。

  10:34336034.574[T1]debug com . example . test . test1-T1线程获得锁.

  @ SLF 4j public class Test1 { private static final Object lock=new Object();public static void main(string[]args)引发中断的异常{ new thread(()-{ synchronized(lock){ log . debug( t1 thread gets lock . )));请尝试{ lock . wait();} catch(interrupted exception e){ e . printstacktrace();} } }, t1 )。start();//等待1s,让线程执行t1线程thread . sleep(1000);//无法获取锁,因为t1线程没有释放锁synchronized (lock){ log.debug(主线程想要获取锁. ));}}}执行wait()方法后的结果:线程t1等待20s,线程t1等待的同时,主线程获取锁。

  10336036:22.723[T1]debug com . example . test . test1-T1线程获取锁.10336036336023.721[main]debug com . example . test . test1-主线程想要获取锁。

  00-1010场景:有些孩子想进房间用算盘(CPU)算,于是老王(操作系统)用了一把锁(sy

  nchronized)让同一时间只有一个小孩能进入房间使用算盘,于是他们排队进入房间。

  (1) 小南最先获取到了锁,进入到房间内,但是由于条件不满足(没烟干不了活),小南不能继续进行计算 ,但小南如果一直占用着锁,其它人就得一直阻塞,效率太低。

  

 

  (2) 于是老王单开了一间休息室(调用 wait 方法),让小南到休息室(WaitSet)等着去了,这时锁释放开, 其它人可以由老王随机安排进屋

  (3) 直到小M将烟送来,大叫一声 [ 你的烟到了 ] (调用 notify 方法)

  

 

  (4) 小南于是可以离开休息室,重新进入竞争锁的队列

  

 

  下面我们看如何正确的实现这个场景

  

 

  

2.1 问题1

@Slf4jpublic class Test2 { private static final Object room = new Object(); // 是否有烟,默认没有 private static boolean hasCigarette = false; public static void main(String[] args) throws InterruptedException { new Thread(()->{ synchronized (room){ log.debug("有烟没?[{}]", hasCigarette); if(!hasCigarette){ log.debug("没烟,先歇会!"); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } } log.debug("有烟没?[{}]", hasCigarette); if(hasCigarette){ log.debug("有烟,[{}],可以开始干活了", hasCigarette); } } },"小南").start(); // 其他5个线程也想获取锁进入房间 for(int i=0;i<5;i++){ new Thread(()->{ synchronized (room){ log.debug("可以开始干活了"); } },"其他人").start(); } // 主线程等待1s Thread.sleep(1000); // 因为小南线程使用sleep()方法,因此他在睡眠期间并不释放锁,送烟的没办法拿到锁进入房间送烟 new Thread(()->{ synchronized (room){ hasCigarette = true; } },"送烟的").start(); }}

执行结果:

 

  

11:10:50.556 [小南] DEBUG com.example.test.Test2 - 有烟没?[false]11:10:50.565 [小南] DEBUG com.example.test.Test2 - 没烟,先歇会!11:10:52.565 [小南] DEBUG com.example.test.Test2 - 有烟没?[false]11:10:52.565 [其他人] DEBUG com.example.test.Test2 - 可以开始干活了11:10:52.565 [其他人] DEBUG com.example.test.Test2 - 可以开始干活了11:10:52.565 [其他人] DEBUG com.example.test.Test2 - 可以开始干活了11:10:52.565 [其他人] DEBUG com.example.test.Test2 - 可以开始干活了11:10:52.565 [其他人] DEBUG com.example.test.Test2 - 可以开始干活了

 

  

(1) 小南线程在睡眠期间并不释放锁,因此其他线程线程也没办法获取到锁进入房间,送烟线程就没办法送烟;

 

  (2) 其它干活的线程,都要一直阻塞,效率太低 ;

  要解决上述的问题,需要使用wait-notify机制

  

 

  

2.2 问题2

@Slf4jpublic class Test2 { private static final Object room = new Object(); // 是否有烟,默认没有 private static boolean hasCigarette = false; public static void main(String[] args) throws InterruptedException { new Thread(()->{ synchronized (room){ log.debug("有烟没?[{}]", hasCigarette); if(!hasCigarette){ log.debug("没烟,先歇会!"); try { room.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } log.debug("有烟没?[{}]", hasCigarette); if(hasCigarette){ log.debug("有烟,[{}],可以开始干活了", hasCigarette); } } },"小南").start(); // 其他5个线程也想获取锁进入房间 for(int i=0;i<5;i++){ new Thread(()->{ synchronized (room){ log.debug("可以开始干活了"); } },"其他人").start(); } // 主线程等待1s Thread.sleep(1000); // 送烟的,唤醒正在睡眠的小南线程 new Thread(()->{ synchronized (room){ hasCigarette = true; room.notify(); } },"送烟的").start(); }}

执行结果:

 

  

11:21:36.775 [小南] DEBUG com.example.test.Test2 - 有烟没?[false]11:21:36.780 [小南] DEBUG com.example.test.Test2 - 没烟,先歇会!11:21:36.780 [其他人] DEBUG com.example.test.Test2 - 可以开始干活了11:21:36.780 [其他人] DEBUG com.example.test.Test2 - 可以开始干活了11:21:36.781 [其他人] DEBUG com.example.test.Test2 - 可以开始干活了11:21:36.781 [其他人] DEBUG com.example.test.Test2 - 可以开始干活了11:21:36.781 [其他人] DEBUG com.example.test.Test2 - 可以开始干活了11:21:37.773 [小南] DEBUG com.example.test.Test2 - 有烟没?[true]11:21:37.774 [小南] DEBUG com.example.test.Test2 - 有烟,[true],可以开始干活了

 

  

解决了其他线程阻塞问题,但是如果有其他线程也在等待呢?就是说等待的线程不止小南一个,那么会不会唤醒错了呢?

 

  

 

  

2.3 问题3

@Slf4jpublic class Test2 { private static final Object room = new Object(); // 是否有烟,默认没有 private static boolean hasCigarette = false; // 是否有外卖,默认没有 static boolean hasTakeout = false; public static void main(String[] args) throws InterruptedException { new Thread(()->{ synchronized (room){ log.debug("有烟没?[{}]", hasCigarette); if(!hasCigarette){ log.debug("没烟,先歇会!"); try { room.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } log.debug("有烟没?[{}]", hasCigarette); if(hasCigarette){ log.debug("有烟,[{}],可以开始干活了", hasCigarette); }else { log.debug("没干成活.."); } } },"小南").start(); new Thread(()->{ synchronized (room){ log.debug("有外卖没?[{}]", hasTakeout); if(!hasTakeout){ log.debug("没外卖,先歇会!"); try { room.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } log.debug("有外卖没?[{}]", hasTakeout); if(hasTakeout){ log.debug("有外卖,[{}],可以开始干活了", hasTakeout); }else{ log.debug("没干成活.."); } } },"小女").start(); // 主线程等待1s Thread.sleep(1000); new Thread(()->{ synchronized (room){ hasTakeout = true; log.debug("外卖到了...."); room.notify(); } },"送外卖的").start(); }}

执行结果:送外卖的应该叫醒小女但是却把小南叫醒了

 

  

11:31:50.989 [小南] DEBUG com.example.test.Test2 - 有烟没?[false]11:31:50.994 [小南] DEBUG com.example.test.Test2 - 没烟,先歇会!11:31:50.994 [小女] DEBUG com.example.test.Test2 - 有外卖没?[false]11:31:50.994 [小女] DEBUG com.example.test.Test2 - 没外卖,先歇会!11:31:51.987 [送外卖的] DEBUG com.example.test.Test2 - 外卖到了....11:31:51.988 [小南] DEBUG com.example.test.Test2 - 有烟没?[false]11:31:51.988 [小南] DEBUG com.example.test.Test2 - 没干成活..

 

  

notify 只能随机唤醒一个 WaitSet 中的线程,这时如果有其它线程也在等待,那么就可能唤醒不了正确的线程,称之为【虚假唤醒】

 

  

 

  

2.4 问题4

解决方法:改为 notifyAll

 

  

new Thread(()->{ synchronized (room){ hasTakeout = true; log.debug("外卖到了...."); room.notifyAll(); }},"送外卖的").start();

执行结果:

 

  

11:34:24.789 [小南] DEBUG com.example.test.Test2 - 有烟没?[false]11:34:24.798 [小南] DEBUG com.example.test.Test2 - 没烟,先歇会!11:34:24.798 [小女] DEBUG com.example.test.Test2 - 有外卖没?[false]11:34:24.802 [小女] DEBUG com.example.test.Test2 - 没外卖,先歇会!11:34:25.794 [送外卖的] DEBUG com.example.test.Test2 - 外卖到了....11:34:25.794 [小女] DEBUG com.example.test.Test2 - 有外卖没?[true]11:34:25.794 [小女] DEBUG com.example.test.Test2 - 有外卖,[true],可以开始干活了11:34:25.794 [小南] DEBUG com.example.test.Test2 - 有烟没?[false]11:34:25.795 [小南] DEBUG com.example.test.Test2 - 没干成活..

 

  

从结果可以看出小女干成活,小南没有干成活。既然送烟的没到,小南应该继续等待才行,等送烟的来了再干活。

 

  用 notifyAll 仅解决某个线程的唤醒问题,但使用 if + wait 判断仅有一次机会,一旦条件不成立,就没有重新判断的机会了

  

 

  

2.5 最终结果

@Slf4jpublic class Test2 { private static final Object room = new Object(); // 是否有烟,默认没有 private static boolean hasCigarette = false; // 是否有外卖,默认没有 static boolean hasTakeout = false; public static void main(String[] args) throws InterruptedException { new Thread(()->{ synchronized (room){ log.debug("有烟没?[{}]", hasCigarette); while (!hasCigarette){ log.debug("没烟,先歇会!"); try { room.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } log.debug("有烟没?[{}]", hasCigarette); if(hasCigarette){ log.debug("有烟,[{}],可以开始干活了", hasCigarette); }else { log.debug("没干成活.."); } } },"小南").start(); new Thread(()->{ synchronized (room){ log.debug("有外卖没?[{}]", hasTakeout); if(!hasTakeout){ log.debug("没外卖,先歇会!"); try { room.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } log.debug("有外卖没?[{}]", hasTakeout); if(hasTakeout){ log.debug("有外卖,[{}],可以开始干活了", hasTakeout); }else{ log.debug("没干成活.."); } } },"小女").start(); // 主线程等待1s Thread.sleep(1000); // 送烟的,唤醒正在睡眠的小南线程 new Thread(()->{ synchronized (room){ hasTakeout = true; log.debug("外卖到了...."); room.notifyAll(); } },"送外卖的").start(); }}@Slf4jpublic class Test2 { private static final Object room = new Object(); // 是否有烟,默认没有 private static boolean hasCigarette = false; // 是否有外卖,默认没有 static boolean hasTakeout = false; public static void main(String[] args) throws InterruptedException { new Thread(()->{ synchronized (room){ log.debug("有烟没?[{}]", hasCigarette); while (!hasCigarette){ log.debug("没烟,先歇会!"); try { room.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } log.debug("有烟没?[{}]", hasCigarette); if(hasCigarette){ log.debug("有烟,[{}],可以开始干活了", hasCigarette); }else { log.debug("没干成活.."); } } },"小南").start(); new Thread(()->{ synchronized (room){ log.debug("有外卖没?[{}]", hasTakeout); if(!hasTakeout){ log.debug("没外卖,先歇会!"); try { room.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } log.debug("有外卖没?[{}]", hasTakeout); if(hasTakeout){ log.debug("有外卖,[{}],可以开始干活了", hasTakeout); }else{ log.debug("没干成活.."); } } },"小女").start(); // 主线程等待1s Thread.sleep(1000); // 送烟的,唤醒正在睡眠的小南线程 new Thread(()->{ synchronized (room){ hasTakeout = true; log.debug("外卖到了...."); room.notifyAll(); } },"送外卖的").start(); }}

执行结果:当没烟的时候,小南线程继续等待,等待下一次判断有烟的时候再干活

 

  

11:38:36.206 [小南] DEBUG com.example.test.Test2 - 有烟没?[false]11:38:36.212 [小南] DEBUG com.example.test.Test2 - 没烟,先歇会!11:38:36.212 [小女] DEBUG com.example.test.Test2 - 有外卖没?[false]11:38:36.212 [小女] DEBUG com.example.test.Test2 - 没外卖,先歇会!11:38:37.205 [送外卖的] DEBUG com.example.test.Test2 - 外卖到了....11:38:37.205 [小女] DEBUG com.example.test.Test2 - 有外卖没?[true]11:38:37.205 [小女] DEBUG com.example.test.Test2 - 有外卖,[true],可以开始干活了11:38:37.205 [小南] DEBUG com.example.test.Test2 - 没烟,先歇会!

 

  

使用wait-notify的正确姿势:

 

  

synchronized(lock) { while(条件不成立) { lock.wait(); }}//另一个线程synchronized(lock) { lock.notifyAll();}

调用wait()和notify()系列方法进行线程通信的要点如下:

 

  (1) 调用某个同步对象locko的wait()和notify()类型方法前,必须要取得这个锁对象的监视锁,所以wait()和notify()类型方法必须放在synchronized(locko)同步块中,如果没有获得监视锁,JVM就会报IllegalMonitorStateException异常。

  (2) 调用wait()方法时使用while进行条件判断,如果是在某种条件下进行等待,对条件的判断就不能使用if语句做一次性判断,而是使用while循环进行反复判断。只有这样才能在线程被唤醒后继续检查wait的条件,并在条件没有满足的情况下继续等待。

  

 

  

总结

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注盛行IT的更多内容!

 

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: