notify 和 notifyAll 的区别

本文探讨Java中多线程的notify和notifyAll方法的区别。notify只会唤醒一个等待线程,而notifyAll会唤醒所有等待线程。线程在调用wait()后进入等待池,通过notify或notifyAll进入锁池竞争对象锁。高优先级线程竞争到锁的概率较大,未获得锁的线程会留在锁池中,直到再次wait或获得锁。
摘要由CSDN通过智能技术生成

先上代码:

package com.jorchi.thr;

import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedList;

/**
 * notify 和 notifyAll
 * notify :只唤醒一个等待线程中的线程
 * notifyAll: 唤醒所有等待线程中的线程
 *
 */
public class TestNotifyNotifyAll {

    /** 锁对象 */
    // private static Object LOCK = new Object();
    /** 锁对象 - 这里放一个队列 */
    static LinkedList LOCK = new LinkedList<Long>();

    public static void main(String[] args) {
        // 消费者线程
        Thread a1 = new Thread(new ConsumerThread(LOCK));
        Thread a2 = new Thread(new ConsumerThread(LOCK));
        Thread a3 = new Thread(new ConsumerThread(LOCK));
        /*Thread a4 = new Thread(new RunnableImplA(LOCK));
        Thread a5 = new Thread(new RunnableImplA(LOCK));*/
        a1.start();
        a2.start();
        a3.start();
        /*a4.start();
        a5.start();*/

        // 等线程 A 都跑起来
        try {Thread.sleep(5000); } catch(Throwable t){}

        // 生产者线程
        Thread b1 = new Thread(new ProducerThread(LOCK));
        b1.start();
    }
}

/**
 * 演示用于消费者
 */
class ConsumerThread implements Runnable {

    /** 其实跟 TestNotifyNotifyAll.LOCK 是同一个对象 */
    private Object obj;

    public ConsumerThread(Object obj) {
        this.obj = obj;
    }

    public void run() {
        System.out.println("run on RunnableImplA "+new Date());
        // synchronized 对象锁,同一时刻最多只有一个线程可以获取到该对象锁
        synchronized (obj) {
            System.out.println("RunnableImplA 拿到对象锁,1秒后将处于等待状态,线程名称:"+Thread.currentThread().getName()+" "+new Date());
            try {
                Thread.sleep(1000);
                // 发现库存不足
                if (TestNotifyNotifyAll.LOCK.isEmpty()) {
                    // 调用 wait,进入对象等待池,线程处于等待状态
                    obj.wait();
                    System.out.println("RunnableImplA 线程被唤醒且拿到对象锁,线程名称:" + Thread.currentThread().getName() + " " + new Date());
                }

                System.out.println("RunnableImplA 进行消费,1秒后才释放对象锁,线程名称:" + Thread.currentThread().getName() + " " + new Date());
                Thread.sleep(1000);

                if (TestNotifyNotifyAll.LOCK.isEmpty()) {
                    System.err.println("逻辑处理错误!!!队列为空如何消费?");
                    return;
                }
                System.out.println(TestNotifyNotifyAll.LOCK.removeFirst());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("RunnableImplA 拿到对象锁,释放对象锁,线程名称:"+Thread.currentThread().getName()+" "+new Date());
        }
    }
}

/**
 * 演示用于生产者
 */
class ProducerThread implements Runnable {

    /** 其实跟 TestNotifyNotifyAll.LOCK 是同一个对象 */
    private Object lockObj;

    public ProducerThread(Object obj) {
        this.lockObj = obj;
    }

    public void run() {
        System.out.println("run on RunnableImplB");

        // 一次生产一个
        doProductOne();
        doProductOne();
        doProductOne();
        System.out.println("OK.");
    }

    /**
     * 一次生产一个
     */
    private void doProductOne() {
        try {
            System.out.println("RunnableImplB 正在生产中,预计4秒后生产完成 ...");
            Thread.sleep(4000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        synchronized (lockObj) {
            // 已经拿到对象锁,把生产的数据放入队列
            TestNotifyNotifyAll.LOCK.add(System.currentTimeMillis());

            // notify :只唤醒一个等待线程中的线程
            System.out.println("RunnableImplB 已生产完成,并且已经拿到对象锁,call notify "+new Date());
            lockObj.notify();

            // notifyAll: 唤醒所有等待线程中的线程
            /*System.out.println("RunnableImplB 已生产完成,并且已经拿到对象锁,call notifyAll 唤醒所有等待线程中的线程"+new Date());
            lockObj.notifyAll();*/

            // System.out.println("RunnableImplB 为了演示,2秒后才释放对象锁"+new Date());
            // try {Thread.sleep(2000); } catch (Throwable e){}

            // notify() 和 notifyAll() 本线程都不会释放它自己的对象锁,
            // 直到该synchronized包裹的方法执行完以后,才会释放锁
            System.out.println("RunnableImplB 释放对象锁"+new Date());
        }
    }
}

notify :只唤醒一个等待线程中的线程

notifyAll: 唤醒所有等待线程中的线程

读懂并运行上面的例子,再来理解下面这些技术性解释

先解释两个概念。

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

  • 锁池:只有获取了对象的锁,线程才能执行对象的 synchronized 代码,对象的锁每次只有一个线程可以获得,其他线程只能在锁池中等待

然后再来说notify和notifyAll的区别

  • 如果线程调用了对象的 wait()方法,那么线程便会处于该对象的等待池中,等待池中的线程不会去竞争该对象的锁。

  • 当有线程调用了对象的 notifyAll()方法(唤醒所有 wait 线程)或 notify()方法(只随机唤醒一个 wait 线程),被唤醒的的线程便会进入该对象的锁池中,锁池中的线程会去竞争该对象锁。也就是说,调用了notify后只要一个线程会由等待池进入锁池,而notifyAll会将该对象等待池内的所有线程移动到锁池中,等待锁竞争

  • 优先级高的线程竞争到对象锁的概率大,假若某线程没有竞争到该对象锁,它还会留在锁池中,唯有线程再次调用 wait()方法,它才会重新回到等待池中。而竞争到对象锁的线程则继续往下执行,直到执行完了 synchronized 代码块,它会释放掉该对象锁,这时锁池中的线程会继续竞争该对象锁。

notify/notifyAll和wait方法,在使用这3个方法时,必须处于synchronized代码块或者synchronized方法中,否则就会抛出IllegalMonitorStateException异常,这是因为调用这几个方法前必须拿到当前对象的监视器monitor对象,也就是说notify/notifyAll和wait方法依赖于monitor对象

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

sugarTan

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值