step1

  1. public class TestCorrectPostureStep1 {
  2. static final Object room = new Object();
  3. static boolean hasCigarette = false;//有没有烟
  4. static boolean hasTakeout = false;
  5. public static void main(String[] args) throws InterruptedException {
  6. new Thread(()->{
  7. synchronized (room){
  8. System.out.println("有烟没?"+hasCigarette);
  9. if(!hasCigarette){
  10. System.out.println("没烟,先歇会儿");
  11. try {
  12. Thread.sleep(2);
  13. } catch (InterruptedException e) {
  14. e.printStackTrace();
  15. }
  16. System.out.println("有烟没?"+hasCigarette);
  17. if(hasCigarette){
  18. System.out.println("可以开始干活了");
  19. }
  20. }
  21. }
  22. },"小南").start();
  23. for(int i=0;i<5;i++){
  24. new Thread(()->{
  25. synchronized (room){
  26. System.out.println("可以干活了");
  27. }
  28. },"其他人").start();
  29. }
  30. Thread.sleep(1);
  31. new Thread(()->{
  32. hasCigarette=true;
  33. System.out.println("烟送到了嗷!");
  34. },"送烟的").start();
  35. }
  36. }

输出:

image.png

  • 其它干活的线程,要被一直阻塞,效率太低
  • 小南线程必须睡足2s后才能醒来,就算烟提前送到,也无法立刻醒来
  • 加了synchronized(room)后,就好比小南在里边反锁了门睡觉,烟根本没法送进门,main没加synchronized就好像main线程是翻窗户进来的
  • 解决方法,使用wait-notify机制

step2

对step1改进,将sleep改成wait-notify机制

  1. public class TestCorrectPostureStep1 {
  2. static final Object room = new Object();
  3. static boolean hasCigarette = false;//有没有烟
  4. static boolean hasTakeout = false;
  5. public static void main(String[] args) throws InterruptedException {
  6. new Thread(()->{
  7. synchronized (room){
  8. System.out.println("有烟没?"+hasCigarette);
  9. if(!hasCigarette){
  10. System.out.println("没烟,先歇会儿");
  11. try {
  12. room.wait();
  13. } catch (InterruptedException e) {
  14. e.printStackTrace();
  15. }
  16. System.out.println("有烟没?"+hasCigarette);
  17. if(hasCigarette){
  18. System.out.println("可以开始干活了");
  19. }
  20. }
  21. }
  22. },"小南").start();
  23. for(int i=0;i<5;i++){
  24. new Thread(()->{
  25. synchronized (room){
  26. System.out.println("可以干活了");
  27. }
  28. },"其他人").start();
  29. }
  30. Thread.sleep(1);
  31. new Thread(()->{
  32. synchronized (room){
  33. hasCigarette=true;
  34. System.out.println("烟送到了嗷!");
  35. room.notify();//唤醒一个线程
  36. }
  37. },"送烟的").start();
  38. }
  39. }

输出:

image.png

  • 解决了其它干活的线程的阻塞问题
  • 但如果有其它线程也在wait,那么notify可能不会将”小南”线程唤醒,因为notify是选择某一个线程唤醒。

step3

测试如果多个线程处于等待状态,notify是否会将特定线程唤醒。

  1. public class TestCorrectPostureStep1 {
  2. static final Object room = new Object();
  3. static boolean hasCigarette = false;//有没有烟
  4. static boolean hasTakeout = false;
  5. public static void main(String[] args) throws InterruptedException {
  6. /*小南线程,等待烟*/
  7. new Thread(()->{
  8. synchronized (room){
  9. System.out.println("有烟没?"+hasCigarette);
  10. if(!hasCigarette){
  11. System.out.println("没烟,先歇会儿");
  12. try {
  13. room.wait();
  14. } catch (InterruptedException e) {
  15. e.printStackTrace();
  16. }
  17. System.out.println("有烟没?"+hasCigarette);
  18. if(hasCigarette){
  19. System.out.println("可以开始干活了");
  20. }else{
  21. System.out.println("没干成活");
  22. }
  23. }
  24. }
  25. },"小南").start();
  26. /*小女线程,等待外卖*/
  27. new Thread(()->{
  28. synchronized (room){
  29. Thread thread = Thread.currentThread();
  30. System.out.println("外卖送到没?"+hasTakeout);
  31. if(!hasTakeout){
  32. System.out.println("没外卖!先歇会儿");
  33. try {
  34. room.wait();
  35. } catch (InterruptedException e) {
  36. e.printStackTrace();
  37. }
  38. }
  39. System.out.println("外卖送到没?"+hasTakeout);
  40. if (hasTakeout){
  41. System.out.println("可以干活了");
  42. }
  43. else{
  44. System.out.println("没干成活");
  45. }
  46. }
  47. },"小女").start();
  48. Thread.sleep(1);
  49. /*送外卖线程*/
  50. new Thread(()->{
  51. synchronized (room){
  52. hasTakeout=true;
  53. System.out.println("外卖到了嗷!");
  54. room.notify();
  55. }
  56. },"送外卖的").start();
  57. }
  58. }

缺点:送外卖线程如果将小南线程唤醒,那么送烟线程没有执行,还是没干成活。属于虚假唤醒,错误地将小南线程唤醒,又没有满足送烟的条件。
一个改进的方式是:改用notifyAll方法,但是虽然将小女线程唤醒,还是会将小南线程虚假唤醒。

step4

step3的问题点在于”小南”线程当被虚假唤醒后,线程即会继续向下执行,解决办法很简单,对条件是否满足的判断从if改成while即可

  1. while(!hasCigarette){
  2. System.out.println("没烟,先歇会儿");
  3. try {
  4. room.wait();
  5. } catch (InterruptedException e) {
  6. e.printStackTrace();
  7. }
  8. System.out.println("有烟没?"+hasCigarette);
  9. if(hasCigarette){
  10. System.out.println("可以开始干活了");
  11. }else{
  12. System.out.println("没干成活");
  13. }
  14. }

当没满足条件时,则会继续wait,相当于你虚假唤醒我,那我继续睡就好了。

所以综上,wait-notify的正确写法为:

  1. synchronized(lock){
  2. while(条件不成立){
  3. lock.wait();//都进入等待了,肯定是某条件没满足
  4. }
  5. //条件满足,继续执行其它代码
  6. }
  7. //另一个线程
  8. synchronized(lock){
  9. lock.notifyAll();//这里建议唤醒线程都用notifyAll
  10. }