Wait 线程阻塞 与 Notify、NotifyAll 线程唤醒

目录

概述 与 问答

wait + notify 举例

wait + notifyAll 举例


概述 与 问答

1、wait()、notify() 和 notifyAll() 是 Java 超级根类 Object 类中的方法

2、wait()、notify() 和 notifyAll() 方法为 final 修饰,无法被重写

3、调用某个对象的 wait() 方法能让当前线程阻塞,并且当前线程必须拥有此对象的锁(monitor)

4、调用某个对象的 notify() 方法能够唤醒一个正在等待这个对象锁线程,如果有多个线程都在等待这个对象的锁,则随机唤醒其中一个线程;

5、调用某个对象的 notifyAll() 方法能够唤醒所有正在等待这个对象的 monitor 的线程;

常用方法描述

public final native void wait()

public final native void wait(long timeout)

在其他线程调用此对象的 notify() 方法或 notifyAll() 方法前,导致当前线程等待。

wait在等待的时候会释放锁,而Thread的sleep休眠的时候是不会释放锁的。

public final native void notify()

唤醒在此对象监视器上等待的单个线程。如果所有线程都在此对象上等待,则会选择唤醒其中一个线程。选择是任意性的,并在对实现做出决定时发生

public final native void notifyAll()

唤醒在此对象监视器上等待的所有线程。线程通过调用其中一个 wait 方法,在对象的监视器上等待。

为什么wait、notify、notifyAll方法不在thread类里面?

1、主要原因是 Java 提供的锁是对象级的而不是线程级的,每个对象都有锁,通过线程获得。

2、由于 wait,notify 和 notifyAll 都是锁级别的操作,所以把他们定义在 Object 类中,因为锁属于对象。

为什么wait、notify、notifyAll方法要在同步块中调用?

1、Java API 强制要求 wait,notify 和 notifyAll 必须写在 synchronized 同步代码块中,如果不这么做,代码会抛出java.lang.IllegalMonitorStateException 异常。

2、还有一个原因是为了避免 wait 和 notify 之间产生竞态条件。

java中wait和sleep方法的不同?

1、最大的不同是在等待是 wait 会释放锁,而 sleep 一直持有锁。

2、Wait 通常被用于线程间交互,sleep通常被用于暂停执行。

3、wait()方法会释放 CPU执行权和占有的锁。

4、sleep(long)方法仅释放CPU使用权,锁仍然占用;线程被放入超时等待队列,与Thread.yield() 相比,它会使线程较长时间得不到运行。

5、Thread.yield() 方法仅释放CPU执行权,锁仍然占用,线程会被放入就绪队列,会在短时间内再次执行。

6、wait 和 notify、notifyAll 必须配套使用,即必须使用同一把锁调用;

7、wait和notify必须放在一个同步块中调用,wait和notify的对象必须是他们所处同步块的锁对象。

wait + notify 举例

import java.util.logging.Logger;
/**
 * Created by Administrator on 2019/3/11 0011.
 */
public class NotifyAllTest {
    public static final Logger logger = Logger.getAnonymousLogger();//日志处理器

    public static void main(String[] args) throws InterruptedException {
        NotifyAllTest notifyAllTest = new NotifyAllTest();
        /**
         * 创建一个子线程并启动它
         * 因为要保证调用 wait 与 调用 notify、notifyAll的是同一个对象,所以这里将 notifyAllTest 实例作为参数传过去
         * 提醒:调用 wait 与 调用 notify、notifyAll 必须是同一个对象,但可以是任意对象,比如普通的 String 、Array 、Boolean 等等
         */
        MyThread myThread_1 = new MyThread(notifyAllTest);
        myThread_1.start();

        for (int i = 3; i > 0; i--) {
            Thread.sleep(1000);
            logger.info("线程 " + Thread.currentThread().getName() + " " + (i) + " 秒后唤醒子线程");
        }
        //Java 强制要求必须在同步代码块中调用 wait、notify、notifyAll
        //如果不在同步代码块中执行,则会抛出异常:java.lang.IllegalMonitorStateException
        synchronized (notifyAllTest) {
            notifyAllTest.notify();
        }
    }
}

class MyThread extends Thread {
    private static final Logger logger = Logger.getAnonymousLogger();//日志处理器

    private NotifyAllTest notifyAllTest;

    public MyThread(NotifyAllTest notifyAllTest) {
        this.notifyAllTest = notifyAllTest;
    }

    @Override
    public void run() {
        //Java 强制要求必须在同步代码块中调用 wait、notify、notifyAll
        //如果不在同步代码块中执行,则会抛出异常:java.lang.IllegalMonitorStateException
        synchronized (notifyAllTest) {
            try {
                logger.info("线程 " + currentThread().getName() + " 开始阻塞等待...");
                notifyAllTest.wait();
                logger.info("线程 " + currentThread().getName() + " 被重新唤醒...");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
================控制台输出如下=================
三月 11, 2019 11:35:30 上午 com.xyjt.notify.MyThread run
信息: 线程 Thread-1 开始阻塞等待...
三月 11, 2019 11:35:31 上午 com.xyjt.notify.NotifyAllTest main
信息: 线程 main 3 秒后唤醒子线程
三月 11, 2019 11:35:32 上午 com.xyjt.notify.NotifyAllTest main
信息: 线程 main 2 秒后唤醒子线程
三月 11, 2019 11:35:33 上午 com.xyjt.notify.NotifyAllTest main
信息: 线程 main 1 秒后唤醒子线程
三月 11, 2019 11:35:33 上午 com.xyjt.notify.MyThread run
信息: 线程 Thread-1 被重新唤醒...

wait + notifyAll 举例

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.logging.Logger;
/**
 * Created by Administrator on 2019/3/11 0011.
 * @author wangmaoxiong
 */
public class NotifyAllTest {
    /**
     * logger:日志处理器
     * isWait:作为所有线程 wait 等待与 notify、notifyAll 通知的同一对象
     */
    public static final Logger logger = Logger.getAnonymousLogger();
    public static final Boolean isWait = true;

    public static void main(String[] args) throws InterruptedException {

        /**
         * 使用无界线程池创建多个子线程并启动它们
         * 调用 wait 与 调用 notify、notifyAll 必须是同一个对象,但可以是任意对象,比如普通的 String 、Array 、Boolean 等等
         */
        ExecutorService executorService = Executors.newCachedThreadPool();
        for (int i = 0; i < 5; i++) {
            executorService.execute(new MyThread(isWait));
        }

        for (int i = 5; i > 0; i--) {
            Thread.sleep(1000);
            logger.info("线程 " + Thread.currentThread().getName() + " " + (i) + " 秒后唤醒子线程");
        }
        //Java 强制要求必须在同步代码块中调用 wait、notify、notifyAll
        //如果不在同步代码块中执行,则会抛出异常:java.lang.IllegalMonitorStateException
        synchronized (isWait) {
            isWait.notifyAll();//通知isWait对象的所有线程继续向后运行
        }
    }
}

class MyThread extends Thread {

    private static final Logger logger = Logger.getAnonymousLogger();

    private Boolean isWait;

    public MyThread(Boolean isWait) {
        this.isWait = isWait;
    }

    @Override
    public void run() {
        //Java 强制要求必须在同步代码块中调用 wait、notify、notifyAll
        //如果不在同步代码块中执行,则会抛出异常:java.lang.IllegalMonitorStateException
        synchronized (isWait) {
            try {
                logger.info("线程 " + currentThread().getName() + " 开始阻塞等待...");
                isWait.wait();
                logger.info("线程 " + currentThread().getName() + " 被重新唤醒...");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

1、wait 在等待的时候会释放锁,所以上面当第一个线程在 wait 在等待的时候,其它线程立马就能获取锁而进来。

2、当所有的线程都释放了锁在 wait 等待的时候,如果 main 方法此时使用 notifyAll 方法通过大家继续运行的时候,这个时候大家如何重新获取锁按顺序运行呢?它们会再次争夺锁确保了至少有一个线程能继续运行。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

蚩尤后裔-汪茂雄

芝兰生于深林,不以无人而不芳。

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

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

打赏作者

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

抵扣说明:

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

余额充值