java并发编程(十)synchronized原理之wait/n

作者: 我犟不过你 | 来源:发表于2021-11-30 16:07 被阅读0次

一、为什么需要wait/notify?

我们都知道wati/notify用于线程间的同步。

假设有一把锁lock,此时线程t1来持有这把锁,但是由于其不满足业务条件暂时不能继续执行,如果t2此时来获取锁,发现锁仍被t1占有,则会发生阻塞,造成严重的效率问题,甚至出现死锁。

通过wait/notify,可是是t1先进入WAITING状态,等待t2的唤醒,被唤醒后再去执行对应的操作,这样就避免了t2线程的阻塞,且t1、t2之间的竞争也没有了,大大提升执行效率。

二、wait/notify原理

前面我们讲过Monitor(管程)的结构,其中有一块叫做waitSet的区域,里面存放的是状态为WAITING状态的线程。如下图虚线框中的内容:

Monitor管程 (3).png

涉及到Monitor,相信大家能够知道,只有在重量级锁中,才会有wait/notify。

下面按照上图简单分析下其流程:

  • 假设线程thread1持有了重量级锁,即Owner,但是此时没有准备好,所以调用了wait方法。此时其状态就会变成WAITING,进入到Monitor的WaitSet当中。
  • EntryList和WaitSet当中的线程,都是不占用CPU时间片的。
  • EntryList当中的线程,会等待Owner执行完成后被唤醒。
  • WaitSet当中的线程,会等待Owner执行notify或notifyAll方法进行唤醒,但是并不是直接获取到锁,而是被添加到EntryList当中进行非公平的竞争。

三、wait/notify使用

3.1 常用方法

  • obj.wait() 让当前持有锁的Owner进入Monitor的WaitSet进行等待。
  • obj.wait(long timeout) 让进入 object 监视器的线程到 waitSet 等待,可制定等待超时时间。
  • obj.notify() Owner从正在 waitSet 等待的线程中挑一个唤醒。
  • obj.notifyAll() Owner让正在 waitSet 等待的线程全部唤醒。

以上方法都是Object类的方法,必须要获取该对象锁,才能调用上述方法。

通过下面的例子,演示用法,妈妈正在做饭,爸爸和小A等着吃饭,爸爸发现饭没有做好,要去睡一会;小A发现饭没有做好,要去玩一会儿。妈妈做好饭了,叫小A和爸爸吃饭。饭后,小A去写作业了,爸爸去钓鱼了:

    /**
     * 吃了吗
     */
    static boolean EATEN_YET = false;

    static Object lock = new Object();

    public static void main(String[] args) {

        new Thread(() -> {
            synchronized (lock) {
                while (true) {
                    if (EATEN_YET) {
                        System.out.println(Thread.currentThread().getName() + ":我要开始写作业了!!");
                        break;
                    } else {
                        try {
                            System.out.println(Thread.currentThread().getName() + ":吃饭前我先玩一会儿!!");
                            lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }

        }, "小A").start();

        new Thread(() -> {
            synchronized (lock) {
                while (true) {
                    if (EATEN_YET) {
                        System.out.println(Thread.currentThread().getName() + ":我要去钓鱼了!!");
                        break;
                    } else {
                        try {
                            System.out.println(Thread.currentThread().getName() + ":吃饭前我先睡一会儿!!");
                            lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }

        }, "爸爸").start();

        new Thread(() -> {
            synchronized (lock) {
                try {
                    TimeUnit.MILLISECONDS.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + ":开饭了!!");
                EATEN_YET = true;
                lock.notifyAll();
            }

        }, "妈妈").start();
    }

结果:

小A:吃饭前我先玩一会儿!!
爸爸:吃饭前我先睡一会儿!!
妈妈:开饭了!!
爸爸:我要去钓鱼了!!
小A:我要开始写作业了!!

相关文章

网友评论

    本文标题:java并发编程(十)synchronized原理之wait/n

    本文链接:https://www.haomeiwen.com/subject/odjdxrtx.html