飞道的博客

notify和notifyAll的区别

243人阅读  评论(0)

一、我们先来了解两个概念

  • 锁池EntryList
  • 等待池WaitSet

锁池

假设线程A已经拥有了某个对象(不是类)的锁,而其它线程B、C想要调用这个对象的某个synchronized方法(或者块),由于B、C线程在进入对象的synchronized方法(或者块)之前必须先获得该对象锁的拥有权,而恰巧该对象的锁目前正在被线程A所占用,此时B、C线程就会被阻塞,进入一个地方去等待锁的释放,这个地方就是该对象的锁池。

等待池

假设线程A调用了某个对象的wait()方法,线程A就会释放该对象的锁,同时线程A就进入到该对象的等待池中,进入到等待池中的线程不会去竞争该对象的锁

二、notify和notifyAll的区别

首先呢,两者都能够唤醒执行了wait()的线程,不同点如下:

  • notifyAll会让所有处于等待池的线程全部进入锁池去竞争获取锁的机会
  • notify只会随机选取一个处于等待池中的线程进入锁池去竞争获取锁的机会

2.1、唤醒wait()后的线程测试

基于上一个测试——sleep和wait 的区别测试(线程中sleep()和wait()的差别),我们将wait设置为无限期等待,需要主动进行唤醒

没加notify/notifyAll

package com.mtli.thread;

/**
 * @Description:
 * @Author: Mt.Li
 * @Create: 2020-05-05 07:56
 */
public class WaitSleepDemo {
    public static void main(String[] args) {
        final Object lock = new Object();
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("线程A正在等待获取同步锁");
                try {
                    synchronized (lock) {
                        System.out.println("线程A已经获取到锁");
                        Thread.sleep(20);
                        System.out.println("线程A执行wait方法,进入无线等待,等待唤醒");
                        lock.wait();
                        System.out.println("线程A执行完毕");
                    }
                }catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("线程B正在等待获取同步锁");
                try {
                    synchronized (lock) {
                        System.out.println("线程B已经获取到锁");
                        Thread.sleep(10);
                        System.out.println("线程B执行sleep方法");
                        System.out.println("线程B执行完毕,需要唤醒A线程");
                       
                    }
                }catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

}

// 运行结果:

线程A正在等待获取同步锁
线程A已经获取到锁
线程B正在等待获取同步锁
线程A执行wait方法,进入无线等待,等待唤醒
线程B已经获取到锁
线程B执行sleep方法
线程B执行完毕,需要唤醒A线程

线程B没有主动唤醒A线程,哪怕是线程B执行完,线程A也还在等待中。

这时,程序会一直在执行,我们加上两个方法中的其中一个——notifyAll()

package com.mtli.thread;

/**
 * @Description:
 * @Author: Mt.Li
 * @Create: 2020-05-05 07:56
 */
public class WaitSleepDemo {
    public static void main(String[] args) {
        final Object lock = new Object();
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("线程A正在等待获取同步锁");
                try {
                    synchronized (lock) {
                        System.out.println("线程A已经获取到锁");
                        Thread.sleep(20);
                        System.out.println("线程A执行wait方法,进入无线等待,等待唤醒");
                        lock.wait();
                        System.out.println("线程A执行完毕");
                    }
                }catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("线程B正在等待获取同步锁");
                try {
                    synchronized (lock) {
                        System.out.println("线程B已经获取到锁");
                        Thread.sleep(10);
                        System.out.println("线程B执行sleep方法");
                        System.out.println("线程B执行完毕,需要唤醒A线程");
                        lock.notifyAll();
                    }
                }catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

}

// 运行结果
线程A正在等待获取同步锁
线程A已经获取到锁
线程B正在等待获取同步锁
线程A执行wait方法,进入无线等待,等待唤醒
线程B已经获取到锁
线程B执行sleep方法
线程B执行完毕,需要唤醒A线程
线程A执行完毕

可以看到最后线程A被唤醒了。同样的我们换成notify()

package com.mtli.thread;

/**
 * @Description:
 * @Author: Mt.Li
 * @Create: 2020-05-05 07:56
 */
public class WaitSleepDemo {
    public static void main(String[] args) {
        final Object lock = new Object();
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("线程A正在等待获取同步锁");
                try {
                    synchronized (lock) {
                        System.out.println("线程A已经获取到锁");
                        Thread.sleep(20);
                        System.out.println("线程A执行wait方法,进入无线等待,等待唤醒");
                        lock.wait();
                        System.out.println("线程A执行完毕");
                    }
                }catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("线程B正在等待获取同步锁");
                try {
                    synchronized (lock) {
                        System.out.println("线程B已经获取到锁");
                        Thread.sleep(10);
                        System.out.println("线程B执行sleep方法");
                        System.out.println("线程B执行完毕,需要唤醒A线程");
                        lock.notify();
                    }
                }catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

}

// 运行结果
线程A正在等待获取同步锁
线程A已经获取到锁
线程B正在等待获取同步锁
线程A执行wait方法,进入无线等待,等待唤醒
线程B已经获取到锁
线程B执行sleep方法
线程B执行完毕,需要唤醒A线程
线程A执行完毕

结果是一样的,线程A被唤醒了

2.2、测试两个方法的不同点

package com.mtli.thread.synchronizedDemo;

public class NotificationDemo {
    // 设置volatile类型的标志
    private volatile boolean go = false;

    private synchronized void shouldGo() throws InterruptedException {
        while(go != true){
            System.out.println(Thread.currentThread()
                    + "正在执行wait()");
            wait();
            System.out.println(Thread.currentThread() + " 被唤醒");
        }
        go = false;
    }

    private synchronized void go() {
        while (go == false){
            System.out.println(Thread.currentThread()
                    + " 唤醒当前对象的一个或多个线程");

            go = true;
            notify();
            // notifyAll();
        }

    }

    public static void main(String args[]) throws InterruptedException {
        final NotificationDemo test = new NotificationDemo();

        Runnable waitTask = new Runnable(){

            @Override
            public void run(){
                try {
                    test.shouldGo();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + " 完成执行");
            }
        };

        Runnable notifyTask = new Runnable(){

            @Override
            public void run(){
                test.go();
                System.out.println(Thread.currentThread().getName() + " 完成执行");
            }
        };

        Thread t1 = new Thread(waitTask, "WT1"); //will wait
        Thread t2 = new Thread(waitTask, "WT2"); //will wait
        Thread t3 = new Thread(waitTask, "WT3"); //will wait
        Thread t4 = new Thread(notifyTask,"NT1"); //will notify

        // 开始线程等待
        t1.start();
        t2.start();
        t3.start();

        Thread.sleep(200);

        // 开始唤醒线程
        t4.start();

    }
}

// 执行结果:
Thread[WT1,5,main]正在执行wait()
Thread[WT2,5,main]正在执行wait()
Thread[WT3,5,main]正在执行wait()
Thread[NT1,5,main] 唤醒当前对象的一个或多个线程
Thread[WT1,5,main] 被唤醒
WT1 完成执行
NT1 完成执行

换成notifyAll():

package com.mtli.thread.synchronizedDemo;

public class NotificationDemo {
    // 设置volatile类型的标志
    private volatile boolean go = false;

    private synchronized void shouldGo() throws InterruptedException {
        while(go != true){
            System.out.println(Thread.currentThread()
                    + "正在执行wait()");
            wait();
            System.out.println(Thread.currentThread() + " 被唤醒");
        }
        go = false;
    }

    private synchronized void go() {
        while (go == false){
            System.out.println(Thread.currentThread()
                    + " 唤醒当前对象的一个或多个线程");

            go = true;
            // notify();
            notifyAll();
        }

    }

    public static void main(String args[]) throws InterruptedException {
        final NotificationDemo test = new NotificationDemo();

        Runnable waitTask = new Runnable(){

            @Override
            public void run(){
                try {
                    test.shouldGo();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + " 完成执行");
            }
        };

        Runnable notifyTask = new Runnable(){

            @Override
            public void run(){
                test.go();
                System.out.println(Thread.currentThread().getName() + " 完成执行");
            }
        };

        Thread t1 = new Thread(waitTask, "WT1"); //will wait
        Thread t2 = new Thread(waitTask, "WT2"); //will wait
        Thread t3 = new Thread(waitTask, "WT3"); //will wait
        Thread t4 = new Thread(notifyTask,"NT1"); //will notify

        // 开始线程等待
        t1.start();
        t2.start();
        t3.start();

        Thread.sleep(200);

        // 开始唤醒线程
        t4.start();

    }
}

// 执行结果
Thread[WT1,5,main]正在执行wait()
Thread[WT2,5,main]正在执行wait()
Thread[WT3,5,main]正在执行wait()
Thread[NT1,5,main] 唤醒当前对象的一个或多个线程
Thread[WT3,5,main] 被唤醒
Thread[WT2,5,main] 被唤醒
WT3 完成执行
NT1 完成执行
Thread[WT2,5,main]正在执行wait()
Thread[WT1,5,main] 被唤醒
Thread[WT1,5,main]正在执行wait()

程序中创建线程方法是创建Runnable的实例对象,并且实现run()方法,然后将实例传入Thread的构造器来实现多线程创建。在这里有讲解。

从运行结果我们可以看到两者的区别,一个只唤醒一个等待中的线程,一个唤醒当前对象等待池中所有等待中的线程去竞争锁

以上纯为个人理解,如有不对,请各位看官及时指出(轻喷)


转载:https://blog.csdn.net/weixin_42173451/article/details/105978044
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场