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
线程,也是没有影响的。