Java并发-被动使线程wait/notify

  • 2020 年 2 月 18 日
  • 筆記

一、介绍

 此任务的目标是安全、高效地使一个运行着的线程开始进入wait状态以及从wait状态中唤醒。而此操作都是通过其他线程被动地使当前线程将运行状态的强制转换,具体实现方式不妨就从代码出发吧!

二、代码示例

import java.util.concurrent.atomic.AtomicInteger;    /**   * 实现线程的被动wait,依靠其余线程发出信号进行线程的waitnotify,而不是本身根据线程数目的限定而控制等待和wait.   *   * 以下toWait方法为将当前线程置为wait状态   * toNotify 方法为将当前线程置为唤醒状态   *   * 其中有一个大区别就是一个方法需要同步监视代码块,而另一个不需要   */  public class PessimisticWait {      static final Object MONITOR = new Object();      static AtomicInteger i = new AtomicInteger();        public static void main(String[] args) {          ThreadTest threadTest = new ThreadTest();          threadTest.start();            try {              Thread.sleep(1000);          } catch (InterruptedException e) {              e.printStackTrace();          }          threadTest.toWait();            try {              Thread.sleep(2000);          } catch (InterruptedException e) {              e.printStackTrace();          }            threadTest.toNotify();                try {              Thread.sleep(1000);          } catch (InterruptedException e) {              e.printStackTrace();          }          threadTest.toWait();          }        static class ThreadTest extends Thread {          private boolean flag = false;            @Override          public void run() {                System.out.println("线程进入第一次运行状态");              while (true) {                  synchronized (MONITOR) {                        if (flag) {                          try {                              System.out.println("线程开始wait");                                MONITOR.wait();                          } catch (InterruptedException e) {                              e.printStackTrace();                          }                      }                  }                  try {                      Thread.sleep(100);                  } catch (InterruptedException e) {                      e.printStackTrace();                  }                  i.getAndIncrement();                    System.out.println("当前线程正再执行" + i);              }              }            public void toWait() {                flag = true;          }            public void toNotify(){                synchronized (MONITOR){                  flag =false;                  MONITOR.notify();                  System.out.println("线程wait结束,唤醒线程");                }            }          }  }

控制台输出:

线程进入第一次运行状态  当前线程正再执行1  当前线程正再执行2  当前线程正再执行3  当前线程正再执行4  当前线程正再执行5  当前线程正再执行6  当前线程正再执行7  当前线程正再执行8  当前线程正再执行9  当前线程正再执行10  线程开始wait  线程wait结束,唤醒线程  当前线程正再执行11  当前线程正再执行12  当前线程正再执行13  当前线程正再执行14  当前线程正再执行15  当前线程正再执行16  当前线程正再执行17  当前线程正再执行18  当前线程正再执行19  当前线程正再执行20  线程开始wait

三、代码分析

代码实现的功能:上述代码实现了在main线程中将ThreadTest线程对象通过waitf方法强制进入阻塞状态以及通过notify方法唤醒。

注意事项toWait()toNotify()方法实现了当前线程的进入wait状态以及唤醒操作。但是我问读者朋友们一个问题:为何前者没有使用synchronized,而后者却需要呢?

 首先,我们wait方法一般通过监视器对象来进行调用,如果在toWait()中使用synchronized关键字进行监视器同步代码块处理,那么在main方法中调用时,当前线程为main线程,这样一来只会错将main线程阻塞。所以我们设计一个flag来控制是否使threadTest对象自己来调用wait方法,这就保证了阻塞线程对象的正确性。

 其次,当前由于释放MONITOR对象而阻塞的线程只有一个,即threadTest对象,所以我们只需在同步监视器内调用 MONITOR.notify();方法即能唤醒threadTest线程,并且此方法不与当前运行的线程有关,所以需要在toNotify方法内部上锁时即使当前线程是main线程,也是没有影响的。