Object类中的线程交互,wait、notify、notifyAll

Object对象概述

在说这些方法之前,有一点需要先思考一下,为什么wait,notify,notifyAll这些跟线程有关的方法是封装在Object中而不线程的Thread类呢?这也是一道面试题经常问的内容。

其实答案很简单,之前我们说过,可以把任意的对象作为锁资源进行竞争,而Object是所有类的父类,所以任意对象都可以调用wait()和notify();所以wait和notify属于Object,当然,在jdk1.5之后还出现了各种线程唤醒、阻塞的方法,包括之前提到的LockSupport中的'park',UnPark,和Condition中的Await和signal等等。

  • void notify():唤醒在此对象监视器上等待的单个线程。
  • void notifyAll():唤醒在此对象监视器上等待的所有线程。
  • void wait():导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法。
  • void wait(long timeout):导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者超过指定的时间量。
  • void wait(long timeout, int nanos):导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者其他某个线程中断当前线程,或者已超过某个实际时间量。

wait方法

当一个线程调用了共享锁对象的wait方法之后,该线程就会被阻塞挂起。直到另一个线程调用了该锁对象的notify方法或者notifyAll方法,原本那个调用wait方法的线程才会被唤醒。另外,线程在调用对象的wait方法之前,必须先获取该对象的锁,否则就会抛出一个IllegalMonitorStateException 异常。用代码来说明会比较好理解一点

public class WaitDemo1 {
    public static void main(String[] args) throws InterruptedException {
        Object o = new Object();

        //这里的调用将会抛出一个IllegalMonitorStateException一场
        o.wait();
        //这样的调用才是正确的,在调用之前要先获取o对象锁
        //这里是主线程获取到了锁
        //因此主线程也会被一直阻塞住,因为没有别的线程去唤醒他
        synchronized (o){
            o.wait();
        }
        //或者调用o方法中的一个synchronized方法,也可以获取锁对象
    }
}

另外,wait方法还可以设置超时参数,wait(long timeout)来实现,到了固定的时间后自动唤醒的效果

notify/notifyAll方法方法

当一个调用了锁对象上的notify方法之后,会唤醒一个在该共享变量上调用了wait方法的线程,一个锁对象可能会有多个线程在等待,如果是这种情况的话,notify方法将会随机唤醒一个线程。

另外,被唤醒的线程不代表他被唤醒之后就可以马上继续执行了,唤醒只是将线程唤醒到了就绪状态,他还是要和其他的线程去竞争锁资源,抢cpu执行权才能继续运行。和wait方法一样,要调用notify方法之前也要先获取该对象的锁,则就会抛IllegalMonitorStateException 异常

notifyAll这个方法就相对好理解了,唤醒该共享对象上所有因为调用了wait而阻塞的方法。

看一段代码示例:

public class WaitDemo2 {
    
    public static void main(String[] args) {
        final Object resource =    new Object();

    Thread threadA =   new Thread(() ->{
        synchronized (resource){
            System.out.println("线程A获取了锁");
            try {
                System.out.println("线程A开始wait");
                resource.wait();
                System.out.println("线程A结束wait");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
        });
    Thread threadB =     new Thread(() ->{
        synchronized (resource){
            System.out.println("线程B获取了锁");
            try {
                System.out.println("线程B开始wait");
                resource.wait();
                System.out.println("线程B结束wait");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
        });
    Thread threadC =     new Thread(() ->{

        synchronized (resource){
            resource.notify();
        }
        });

    threadA.start();
    threadB.start();
        //休眠一秒
    Thread.sleep(1000);
    threadC.start();

        try {
            threadA.join();
            threadB.join();
            threadC.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("main 函数结束");
    }
}

这段代码中创建了三个线程,其中线程A和B调用了wait方法,那么这两个都会被阻塞,然后线程C调用了notify方法,于是会随机唤醒AB其中一个线程,但是另一个线程还是处于阻塞状态。因此,输出结果是这样的

线程A获取了锁
线程A开始wait
线程B获取了锁
线程B开始wait
线程A结束wait

由于还有一个线程在阻塞着,又调用了join方法,因此,main函数永远不会停止,但是如果把notify方法改为notifyAll,那么输出结果就是这样的

线程A获取了锁
线程A开始wait
线程B获取了锁
线程B开始wait
线程B结束wait
线程A结束wait
main 函数结束

posted @ 2020-05-26 16:01  穿黑风衣的牛奶  阅读(407)  评论(0编辑  收藏  举报