您的位置 首页 java

Java并发编程系列(二)——彻底理解java的wait和notify机制

简单介绍

  • wait方法

wait方法的作用是使当前执行代码的线程进行等待,它是Object类的方法,该方法用来将当前线程置入等待队列中,并且在wait所在的代码行处停止执行,直到接到通知或被中断为止。

该方法只能在 同步方法或同步块中 调用(即需要先获得对象的监视器锁,一般来说在 synchronized 代码块或者synchronized修饰的方法中使用),否则抛出异常 IllegalMonitorStateException。

在A线程中调用Lock对象的wait方法以后,会释放 Lock 对象的监视器锁,同时将A线程放置于Lock对象的等待队列,A线程进入WAITING状态 。

  • notify/notifyAll方法

notify/notifyAll方法的作用是唤醒执行对象的等待列表中的一个/所有线程,将其唤醒继续工作。

同样的,notify/notifyAll方法也 只能在同步方法或同步块中调用,即在调用前,线程也必须获得该对象的 监视器锁

在B线程中调用Lock对象的notify/notifyAll方法以后,Lock对象等待队列中的A线程从WAITING状态进入BLOCKED状态,而不是直接进入RUNNABLE状态。只有等B线程释放了Lock对象的监视器锁,并且A线程拿到以后,才进入到RUNNABLE状态。所以在编程中,尽量在使用了notify/notifyAll() 后立即释放对象的监视器锁,以便让其他线程获得锁进入RUNNABLE状态。

  • 其他注意事项

wait、notify/notifyAll 方法是Object的本地final方法,无法被重写。

notify 和wait 的顺序不能错,如果A线程先执行notify方法,B线程再执行wait方法,那么B线程是无法被唤醒的。

实例详解

 public class WaitThread extends Thread {

    private Object lock;

    public WaitThread(Object lock) {
        this.lock = lock;
    }

    @ Override 
    public void run() {
        synchronized (lock) {
            System.out.println("WaitThread开始执行run方法");
            try {
                System.out.println("WaitThread开始执行wait方法");
                lock.wait();
                System.out.println("WaitThread被唤醒");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}  
 public class NotifyThread extends Thread {

    private Object lock;

    public NotifyThread(Object lock) {
        this.lock = lock;
    }

    @Override
    public void run() {
        System.out.println("NotifyThread开始执行run方法");
        try {
            System.out.println("NotifyThread睡眠2秒");
             Thread .sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        synchronized (lock) {
            System.out.println("NotifyThread开始执行notify方法");
            lock.notify();
            try {
                System.out.println("NotifyThread执行notify方法后继续睡眠2秒");
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("NotifyThread睡眠2秒结束,并释放对象监视器锁");
        }
    }
}  
 public class Main {
    public static void main(String[] args) {

        Object lock = new Object();

        // 创建2个线程
        WaitThread waitThread = new WaitThread(lock);
        NotifyThread notifyThread = new NotifyThread(lock);
        // 启动他们
        waitThread.start();
        notifyThread.start();
    }
}  
 NotifyThread开始执行run方法
WaitThread开始执行run方法
WaitThread开始执行wait方法
NotifyThread睡眠2秒
NotifyThread开始执行notify方法
NotifyThread执行notify方法后继续睡眠2秒
NotifyThread睡眠2秒结束,并释放对象监视器锁
WaitThread被唤醒  

WaitThread在拿到lock的监视器锁以后调用wait方法。

NotifyThread在启动以后先睡眠2秒,保证了notify方法在wait方法后面。

NotifyThread执行notify方法后继续睡眠2秒,这个时候NotifyThread并没有释放Lock的监视器锁,因此WaitThread处于BLOCKED状态并没有被真正被唤醒。

NotifyThread睡眠2秒结束,并释放对象监视器锁,这个时候NotifyThread取到Lock的监视器锁并进入到RUNNABLE状态。

进阶思考

如果A线程获得了多个对象的监视器锁,然后调用其中1个对象的wait方法,是释放所有对象的锁还是只释放调用的那个对象的锁呢?

我们一起通过一个示例来进行一下测试。

 public class WaitThread extends Thread {

    private Object lock;
    private Object other;

    public WaitThread(Object lock, Object other) {
        this.lock = lock;
        this.other = other;
    }

    @Override
    public void run() {
        synchronized (lock) {
            synchronized (other) {
                System.out.println("WaitThread开始执行run方法");
                try {
                    System.out.println("WaitThread开始执行wait方法");
                    lock.wait();
                    System.out.println("WaitThread被唤醒");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}  
 public class NotifyThread extends Thread {

    private Object lock;
    private Object other;

    public NotifyThread(Object lock, Object other) {
        this.lock = lock;
        this.other = other;
    }

    @Override
    public void run() {
        System.out.println("NotifyThread开始执行run方法");
        try {
            System.out.println("NotifyThread睡眠2秒");
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        synchronized (lock) {
            System.out.println("NotifyThread获得lock锁");

            synchronized (other) {
                System.out.println("NotifyThread获得other锁");
                System.out.println("NotifyThread开始执行notify方法");
                lock.notify();
                try {
                    System.out.println("NotifyThread执行notify方法后继续睡眠2秒");
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("NotifyThread睡眠2秒结束,并释放对象监视器锁");
            }
        }
    }
}  
 public class Main {
    public static void main(String[] args) {

        Object lock = new Object();
        Object other = new Object();

        // 创建2个线程
        WaitThread waitThread = new WaitThread(lock, other);
        NotifyThread notifyThread = new NotifyThread(lock, other);
        // 启动他们
        waitThread.start();
        notifyThread.start();
    }
}  
 WaitThread开始执行run方法
WaitThread开始执行wait方法
NotifyThread开始执行run方法
NotifyThread睡眠2秒
NotifyThread获得lock锁  

WaitThread线程拿到lock和other的对象锁以后,执行了lock的wait方法。NotifyThread线程在睡眠2秒后,仅拿到了lock锁,说明wait方法只释放了被执行对应的锁,这样就造成了 死锁

因此如果使用wait和notify机制时,一定要确认Wait线程和Notify线程获取对象锁的情况,尽量避免在获取多个对象锁的情况下使用,防止造成死锁问题。

文章来源:智云一二三科技

文章标题:Java并发编程系列(二)——彻底理解java的wait和notify机制

文章地址:https://www.zhihuclub.com/188865.shtml

关于作者: 智云科技

热门文章

网站地图