step1
public class TestCorrectPostureStep1 {static final Object room = new Object();static boolean hasCigarette = false;//有没有烟static boolean hasTakeout = false;public static void main(String[] args) throws InterruptedException {new Thread(()->{synchronized (room){System.out.println("有烟没?"+hasCigarette);if(!hasCigarette){System.out.println("没烟,先歇会儿");try {Thread.sleep(2);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("有烟没?"+hasCigarette);if(hasCigarette){System.out.println("可以开始干活了");}}}},"小南").start();for(int i=0;i<5;i++){new Thread(()->{synchronized (room){System.out.println("可以干活了");}},"其他人").start();}Thread.sleep(1);new Thread(()->{hasCigarette=true;System.out.println("烟送到了嗷!");},"送烟的").start();}}
输出:

- 其它干活的线程,要被一直阻塞,效率太低
 - 小南线程必须睡足2s后才能醒来,就算烟提前送到,也无法立刻醒来
 - 加了synchronized(room)后,就好比小南在里边反锁了门睡觉,烟根本没法送进门,main没加synchronized就好像main线程是翻窗户进来的
 - 解决方法,使用wait-notify机制
 
step2
对step1改进,将sleep改成wait-notify机制
public class TestCorrectPostureStep1 {static final Object room = new Object();static boolean hasCigarette = false;//有没有烟static boolean hasTakeout = false;public static void main(String[] args) throws InterruptedException {new Thread(()->{synchronized (room){System.out.println("有烟没?"+hasCigarette);if(!hasCigarette){System.out.println("没烟,先歇会儿");try {room.wait();} catch (InterruptedException e) {e.printStackTrace();}System.out.println("有烟没?"+hasCigarette);if(hasCigarette){System.out.println("可以开始干活了");}}}},"小南").start();for(int i=0;i<5;i++){new Thread(()->{synchronized (room){System.out.println("可以干活了");}},"其他人").start();}Thread.sleep(1);new Thread(()->{synchronized (room){hasCigarette=true;System.out.println("烟送到了嗷!");room.notify();//唤醒一个线程}},"送烟的").start();}}
输出:

- 解决了其它干活的线程的阻塞问题
 - 但如果有其它线程也在wait,那么notify可能不会将”小南”线程唤醒,因为notify是选择某一个线程唤醒。
 
step3
测试如果多个线程处于等待状态,notify是否会将特定线程唤醒。
public class TestCorrectPostureStep1 {static final Object room = new Object();static boolean hasCigarette = false;//有没有烟static boolean hasTakeout = false;public static void main(String[] args) throws InterruptedException {/*小南线程,等待烟*/new Thread(()->{synchronized (room){System.out.println("有烟没?"+hasCigarette);if(!hasCigarette){System.out.println("没烟,先歇会儿");try {room.wait();} catch (InterruptedException e) {e.printStackTrace();}System.out.println("有烟没?"+hasCigarette);if(hasCigarette){System.out.println("可以开始干活了");}else{System.out.println("没干成活");}}}},"小南").start();/*小女线程,等待外卖*/new Thread(()->{synchronized (room){Thread thread = Thread.currentThread();System.out.println("外卖送到没?"+hasTakeout);if(!hasTakeout){System.out.println("没外卖!先歇会儿");try {room.wait();} catch (InterruptedException e) {e.printStackTrace();}}System.out.println("外卖送到没?"+hasTakeout);if (hasTakeout){System.out.println("可以干活了");}else{System.out.println("没干成活");}}},"小女").start();Thread.sleep(1);/*送外卖线程*/new Thread(()->{synchronized (room){hasTakeout=true;System.out.println("外卖到了嗷!");room.notify();}},"送外卖的").start();}}
缺点:送外卖线程如果将小南线程唤醒,那么送烟线程没有执行,还是没干成活。属于虚假唤醒,错误地将小南线程唤醒,又没有满足送烟的条件。
一个改进的方式是:改用notifyAll方法,但是虽然将小女线程唤醒,还是会将小南线程虚假唤醒。
step4
step3的问题点在于”小南”线程当被虚假唤醒后,线程即会继续向下执行,解决办法很简单,对条件是否满足的判断从if改成while即可
while(!hasCigarette){System.out.println("没烟,先歇会儿");try {room.wait();} catch (InterruptedException e) {e.printStackTrace();}System.out.println("有烟没?"+hasCigarette);if(hasCigarette){System.out.println("可以开始干活了");}else{System.out.println("没干成活");}}
当没满足条件时,则会继续wait,相当于你虚假唤醒我,那我继续睡就好了。
所以综上,wait-notify的正确写法为:
synchronized(lock){while(条件不成立){lock.wait();//都进入等待了,肯定是某条件没满足}//条件满足,继续执行其它代码}//另一个线程synchronized(lock){lock.notifyAll();//这里建议唤醒线程都用notifyAll}
