多线程-等待(Wait)和通知(notify)

1.为了支撑多线程之间的协作,JDK提供了两个非常重要的线程接口:等待wait()方法和通知notify()方法。

   这两个方法并不是在Thread类中的,而是输出在Object类。这意味着任何对象都可以调用这两个方法。

 这两个方法如下  

  

  

 

 当在一个对象实例上调用wait()方法后,当前线程就会在这个对象上等待。

 比如,在线程A中,调用了obj.wait()方法,那么线程A就会停止继续执行,转为等待状态。等待到何时结束呢?线程A会一直等到其他线程调用了obj.notity()方法为止。

2.wait()方法和notify()方法究竟是如何工作的呢?

 

 

   如果一个线程调用了object.wait()方法,那么它就会进入object对象的等待队列,这个队列中,可能会有多个线程,因为系统运行多个线程同时等待某一个对象,

  当object.notify()方法被调用的时候,它就会从这个等待队列中随机选择一个线程,并进行唤醒。

  除notity()方法外,Object对象还有一个类似的notifyAll()方法,它和notity方法的功能基本一致,不同的是,它会唤醒在这个等待队列中所有等待的线程,而不是随机一个。

  object.wait()方法并不能随便调用。它必须包含在对象的synchronzied语句中,无论是wait()方法或者notity()方法都需要首先获得目标对象的一个监视器。

 

 

 

 

   如图,其中T1和T2表示两个线程,T1在正确执行wait()方法前,必须获得object对象的监视器,而wait()方法执行之后会释放这个监视器。

这样做的目的是使其他等待在object对象上的线程不至于因为T1的休眠而全部无法正常执行。

  线程T2在notity()方法调用前,也必须获得object对象的监视器。此时T1已经释放了这个监视器。所以T2可以顺利获得object对象的监视器。

接着,T2执行了notify()方法尝试唤醒一个等待线程,这里假设唤醒了T1,T1被唤醒后,要做的第一件事并不是执行后续代码,而是要尝试重新

获得object对象的监视器,而这个监视器也正是T1在wait()方法执行前所持有的那个。

如果暂时无法获得,则T1还必须等待这个监视器。当监视器顺利获得后,T1才可以在真正意义上继续执行。

 

3.为了方便理解,简单的案例:

/**
 * wait和 notify 的学习
 * @author wm
 * @date 2019/9/18 15:15
 */
public class SimpleWN {
    final static Object object = new Object();

    public static class T1 extends  Thread{
        public void run() {
            synchronized (object){
                System.out.println(System.currentTimeMillis()+":T1 start! ");
                System.out.println(System.currentTimeMillis()+":T1 wait for object");
                try {
                    object.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(System.currentTimeMillis()+":T1 end! ");
            }
        }
    }

    public static class T2 extends Thread{
        public void run() {
            synchronized (object){
                System.out.println(System.currentTimeMillis()+":T2 start! notify one thread");
                object.notify();
                System.out.println(System.currentTimeMillis()+":T2 end! ");
                try {
                    object.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public static void main(String[] args) {
        Thread t1 = new T1();
        Thread t2 = new T2();
        t1.start();
        t2.start();
    }
}

 上述代码中,开启了T1和T2两个线程。T1执行了object.wait()方法。执行wait()方法前,T1先申请object的对象锁。

因此,在执行object.wait()时,它持有的是object的对象锁的。wait()方法执行后,T1会进行等待,并释放object对象锁。

T2在执行notity()方法之前也会先获得object的对象锁,这里为了让结果明显,特意在notity()方法执行之后,让T2休眠2秒,

这样做可以更明显地说明,T1在得到notity()方法通知后,还是会先尝试重新获得object的对象锁。

执行结果:

1570675715571:T1 start! 
1570675715571:T1 wait for object
1570675715571:T2 start! notify one thread
1570675715571:T2 end! 
1570675715571:T1 end! 

在T2通知T1继续执行后,T1并不能立即继续执行,而是要等待T2释放object的锁,并重新成功获得锁后,才能继续执行;

 

4.Object.wait()方法和Thread.sleep()方法都可以让线程等待若干时间,除wait()方法可以被唤醒外,

另外一个主要区别就是wait()方法会释放目标对象的锁,而Thread.sleep()方法不会释放任何资源。

 

posted @ 2019-10-10 11:25  超人不会飞er  阅读(1435)  评论(0编辑  收藏  举报