【Java多线程】wait方法和notify方法

目录

前言:

1.wait方法

wait结束等待的条件:

1)wait()不带参数

[2)wait(long timeout)带参数](#2)wait(long timeout)带参数)

3)其他线程调⽤该等待线程的interrupted⽅法,?导致wait抛出 InterruptedException 异常.其他线程调⽤该等待线程的interrupted⽅法,?导致wait抛出 InterruptedException 异常.)

2.notify()方法

notify()⽅法

notifyAll()⽅法

3.wait和sleep的对⽐(⾯试题)


前言:

线程是抢占式执行的,无法预知线程之间的执行顺序。线程是随机调度。

但有时程序员也希望能合理协调多个线程的执行顺序。

因此,在 Java 中使用了等待(wait)和通知(notify)机制,用于在应用层面上干预多个线程的执行顺序。

完成这个协调⼯作, 主要涉及到三个⽅法

  • wait() / wait(long timeout): 让当前线程进⼊等待状态.
  • notify() / notifyAll(): 唤醒在当前对象上等待的线程.

注意:wait,notify,notifyAll都是Object类的方法


1.wait方法

• 使当前执⾏代码的线程进⾏等待.(把线程放到等待队列中)
• 释放当前的锁
• 满⾜⼀定条件时被唤醒,重新尝试获取这个锁.

wait要搭配synchronized来使⽤.脱离synchronized使⽤wait会直接抛出异常.


代码⽰例:观察wait()⽅法使⽤:

wait结束等待的条件:

1)wait()不带参数
java 复制代码
public class demo4 {
    public static void main(String[] args) throws InterruptedException {
        Object object = new Object();
        synchronized (object) {
            System.out.println("等待中");
            object.wait();
            System.out.println("等待结束");
        }
    }
}

这样在执⾏到object.wait()之后就⼀直等待下去,那么程序肯定不能⼀直这么等待下去了

2)**wait(long timeout)**带参数
java 复制代码
public class demo4 {
    public static void main(String[] args) throws InterruptedException {
        Object object = new Object();
        synchronized (object) {
            System.out.println("等待中");
            object.wait(1000);
            System.out.println("等待结束");
        }
    }
}
java 复制代码
等待中
等待结束

Process finished with exit code 0
3)其他线程调⽤该等待线程的interrupted⽅法,?导致wait抛出 InterruptedException 异常.
java 复制代码
public class demo4 {
    public static void main(String[] args) {
        //创建锁对象;
        Object locker = new Object();

        Thread t1 = new Thread(() -> {
            System.out.println("wait前");
            //在 synchronized 代码块中调用 wait 方法;
            synchronized (locker) {
                // wait 方法是由锁对象调用的,调用后,线程释放锁,进入等待队列;
                try {
                    locker.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("wait后");
        });
        t1.start();
        //抛出异常,清除中断标志,唤醒t1线程。
        t1.interrupt();
    }
}

4)使用notify方法唤醒


2.notify()方法

notify⽅法是唤醒等待的线程.

  • ⽅法notify()也要在同步⽅法或同步块中调⽤,该⽅法是⽤来通知那些可能等待该对象的对象锁的其它线程,对其发出通知notify,并使它们重新获取该对象的对象锁。
  • 如果有多个线程等待,则有线程调度器随机挑选出⼀个呈wait状态的线程。(并没有"先来后到")
  • 在notify()⽅法后,当前线程不会⻢上释放该对象锁,要等到执⾏notify()⽅法的线程将程序执⾏完,也就是退出同步代码块之后才会释放对象锁。
notify()⽅法
java 复制代码
public class demo4 {
    public static void main(String[] args) {
        //创建锁对象;
        Object locker = new Object();
        Thread t1 = new Thread(()->{
            System.out.println("t1 wait前!");
            synchronized (locker) {
                try {
                    locker.wait();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
            System.out.println("t1 wait后!");
        });

        Thread t2 = new Thread(()-> {
            try {
                Thread.sleep(1000);
                System.out.println("t2 notify 之前");
                //让 t1 线程有时间进入到wait状态
                synchronized (locker) {
                    locker.notify();
                    System.out.println("t2 notify 之后");
                }
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        });
        t1.start();
        t2.start();
    }
}
java 复制代码
t1 wait前!
t2 notify 之前
t2 notify 之后
t1 wait后!

Process finished with exit code 0

notifyAll()⽅法

使用notifyAll()⽅法可以唤起所有的线程

java 复制代码
    public static void main(String[] args) throws InterruptedException {
        Object locker = new Object();
        Thread t1 = new Thread(()->{
            System.out.println("t1 wait!");
            synchronized (locker) {
                try {
                    locker.wait();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
            System.out.println("t1 over!");
        });
        Thread t2 = new Thread(()->{
            System.out.println("t2 wait!");
            synchronized (locker) {
                try {
                    locker.wait();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
            System.out.println("t2 over!");
        });
        Thread t3 = new Thread(()->{
            System.out.println("t3 wait!");
            synchronized (locker) {
                try {
                    locker.wait();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
            System.out.println("t3 over!");
        });
        Thread t4 = new Thread(()->{
            synchronized (locker) {
                System.out.println("t4 唤起所有线程");
                locker.notifyAll();
            }
        });
        t1.start();
        t2.start();
        t3.start();
        Thread.sleep(1000);
        t4.start();
    }

添加 Thread.sleep(1000) 后,程序会稍微暂停 1 秒钟,确保 t1t2t3 线程已经进入 wait() 方法并且已经释放了 locker 锁。这样,在 t4 线程调用 notifyAll() 时,locker 锁已经被释放,t4 可以成功获取锁并调用 notifyAll() 来唤醒所有等待的线程,从而避免了死锁。


3.wait和sleep的对⽐(⾯试题)

特性 wait() sleep()
来源 Object 类的实例方法 Thread 类的静态方法
锁的释放 会释放对象锁 不释放锁
线程状态 进入等待状态(Waiting) 进入休眠状态(Sleeping)
使用场景 线程间通信与协调(如生产者-消费者) 控制线程暂停时间、模拟延时等
中断处理 抛出 InterruptedException 抛出 InterruptedException
参数 可以指定等待时间(毫秒、纳秒) 可以指定休眠时间(毫秒、纳秒)
是否需要同步 需要在同步代码块或同步方法中调用 不需要同步
影响线程调度 影响线程的调度,直到线程被唤醒 影响线程调度,直到指定时间过后
线程恢复 通过 notify()notifyAll() 唤醒 休眠时间到达后自动恢复

总结:

  1. 调用对象wait()Object 类的一个方法,它必须在同步块或同步方法中调用,用于释放当前线程持有的锁,并使线程进入等待队列,直到其他线程调用同一对象的 notify()notifyAll() 方法。而 sleep()Thread 类的静态方法,线程调用时不需要持有锁,它会让当前线程休眠指定的时间。

  2. 释放锁wait() 会释放对象的锁,而 sleep() 不会释放锁。

  3. 目的wait() 主要用于线程间的通信,线程进入等待状态直到被唤醒;sleep() 主要用于控制线程的暂停时间,通常不用于线程间的协调。


结语: 写博客不仅仅是为了分享学习经历,同时这也有利于我巩固知识点,总结该知识点,由于作者水平有限,对文章有任何问题的还请指出,接受大家的批评,让我改进。同时也希望读者们不吝啬你们的点赞+收藏+关注,你们的鼓励是我创作的最大动力!

相关推荐
军训猫猫头2 分钟前
44.扫雷第二部分、放置随机的雷,扫雷,炸死或成功 C语言
c语言·开发语言
北巷!!3 分钟前
宇信科技JAVA笔试(2024-11-26日 全部AK)
java·开发语言·科技
ᝰꫝꪉꪯꫀ3614 分钟前
JavaWeb——SpringBoot原理
java·开发语言·后端·springboot
Octopus20777 分钟前
【C++】读取数量不定的输入数据
开发语言·c++·笔记·学习
忘梓.7 分钟前
C嘎嘎探索篇:栈与队列的交响:C++中的结构艺术
c语言·开发语言·c++·
HaiFan.8 分钟前
Spring日志
java·spring boot
爬山算法10 分钟前
Tomcat(36)Tomcat的静态资源缓存
java·缓存·tomcat
TPBoreas11 分钟前
手搓一个不用中间件的分表策略
java
、十一、12 分钟前
Tomcat的工作模式是什么?
java·tomcat
killsime14 分钟前
JavaWeb开发 : tomcat+Servlet+JSP
java·servlet·tomcat·javaweb