欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 科技 > IT业 > Java EE初阶——wait 和 notify

Java EE初阶——wait 和 notify

2025/5/17 19:53:45 来源:https://blog.csdn.net/2302_80250536/article/details/147969897  浏览:    关键词:Java EE初阶——wait 和 notify

 1. 线程饥饿

线程饥饿是指一个或多个线程因长期无法获取所需资源(如锁,CPU时间等)而持续处于等待状态,导致其任务无法推进的现象。

典型场景

  1. 优先级抢占

    • 在支持线程优先级的系统中,高优先级线程可能持续抢占CPU资源

    • 导致低优先级线程长期无法获得CPU时间片

  2. 不公平锁竞争

    • 某些线程频繁获取锁,其他线程长期等待

    • 典型场景:某个线程释放锁后立即重新竞争并获得锁

    • 导致其他线程始终处于BLOCKED状态而无法执行

  3. 资源分配不均

    • 某些线程占用大量I/O或内存资源

    • 其他线程因资源不足而无法执行

  4. 线程池配置不当

    • 固定大小线程池中,长任务占用所有线程

    • 短任务无法得到执行机会

关键问题点

  1. 锁获取模式问题

    • 活跃线程释放锁后立即重新请求锁

    • 处于RUNNABLE状态的线程比BLOCKED状态的线程有更快的响应速度

    • 操作系统唤醒BLOCKED线程需要上下文切换,造成竞争劣势

  2. 系统调度机制

    • 默认调度策略可能不利于公平性

    • 缺乏有效的防饥饿机制

  3. 线程状态转换开销

    • BLOCKED→RUNNABLE状态转换需要系统介入

    • 这种转换比保持RUNNABLE状态有更高的延迟

2. wait 和 notify

wait/notify 的本质作用

  • 应用层协作工具wait() 和 notify() 是 Java 提供的应用层线程协作机制,用于控制线程对共享资源的访问顺序,而非直接干预操作系统的线程调度策略。
  • 不改变调度规则:操作系统内核仍按自身调度算法(如轮转法、优先级调度)决定线程何时获得 CPU 时间,wait/notify 无法强制指定某个线程优先执行。

1. wait()⽅法

wait(); 内部做的三件事:

1. 立即释放锁,无需等待同步块结束

2. 线程状态变化RUNNING → WAITING

3. 线程被唤醒后需重新获取锁,获取成功后从 wait() 调用处继续执行。WAITING→ BLOCKED(被唤醒后重新竞争锁)→ RUNNING

线程状态变化后,其他线程就有机会获取锁。

wait() 方法的三种重载形式

方法说明
wait()使当前线程无限期等待,直到另一个线程调用 notify() 或 notifyAll() 方法
wait(long timeout)指定一个超时时间,线程将在超时后自动被唤醒。线程也可以在超时前被 notify() 或 notifyAll() 方法唤醒。
wait(long timeout, int nanos)提供更高精度的超时设置,总超时时间(以纳秒为单位)计算为 1_000_000*timeout + nanos

在 Java 中,调用 wait 方法的对象必须和锁对象一致,这是因为 wait 方法的行为是基于对象的监视器(锁)来实现的。以下是具体解释:

  • 原理:当一个线程调用某个对象的 wait 方法时,该线程会释放它所持有的该对象的锁,并进入等待状态,直到其他线程调用同一个对象的 notify 或 notifyAll 方法来唤醒它。如果调用 wait 方法的对象与获取锁的对象不一致,那么线程在等待时就无法正确地与该锁关联,也就无法按照预期被唤醒,并且可能会导致程序出现逻辑错误。

2. notify()⽅法

方法说明
notify()唤醒等待该对象监视器的一个随机线程。选择唤醒哪个线程是非确定性的,取决于“随机调度”算法
notifyAll()唤醒所有等待该对象监视器的线程。被唤醒的线程会和其他试图获取该对象锁的线程一起竞争锁

调用 notify() 或 notifyAll() 的对象必须与调用 wait() 的对象相同,并且它们必须与 synchronized 使用的锁对象一致,否则会抛出 IllegalMonitorStateException

  1. wait()notify()notifyAll() 必须由同一个对象调用

  2. 必须在 synchronized 块中使用,并且锁对象必须与调用 wait()/notify() 的对象一致

每个 Java 对象都有一个监视器(monitor),也可以理解为锁。当一个线程进入synchronized代码块时,它会获取该代码块所关联对象的锁。wait方法会让当前线程释放这个锁,并进入等待状态,直到其他线程调用同一个对象的notifynotifyAll方法来唤醒它。而notifynotifyAll方法也需要在获取相同对象的锁之后才能调用,这样它们才能准确地唤醒在该对象上等待的线程。如果这三个对象不一致,就会破坏这种线程同步机制,导致程序出现不可预测的结果,例如线程无法被唤醒、死锁等问题。

wait()notify() 和 notifyAll() 方法必须在 synchronized 修饰的代码块或方法中调用,否则会抛出 IllegalMonitorStateException

1. 锁与等待队列的绑定

每个 Java 对象都有两个核心属性:

  • 监视器锁(Monitor):用于实现同步。
  • 等待队列(Wait Set):用于存储调用 wait() 的线程。

wait() 和 notify() 的操作对象是对象的等待队列,而等待队列的状态由来保护。因此,必须先获取锁才能操作等待队列。

2. 原子性与可见性保障
public class SynchronizedDomo8 {public static void main(String[] args) throws InterruptedException {Object object = new Object();// 作为同步锁和 wait/notify 的监视器对象Thread t1 = new Thread(()->{synchronized (object){// 获取 object 的锁System.out.println("t1 线程之前");// ①try {//必须使用同一个对象调用object.wait();// ② 释放锁,进入WAITING状态} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("t1 线程之后");//  ⑦  被唤醒后重新获取锁,继续执行}});Thread t2 = new Thread(()->{try {Thread.sleep(2000);// ③ 休眠 2 秒,确保 t1 先执行并进入 wait()} catch (InterruptedException e) {throw new RuntimeException(e);}synchronized (object){// 获取 object 的锁System.out.println("t2 线程之前");// ④//必须使用同一个对象调用object.notify();  // ⑤ 唤醒t1,但t2仍持有锁System.out.println("t2 线程之后");// ⑥ 同步块结束后释放锁}});t1.start();t2.start();}
}

执行步骤

  1. t1 进入 synchronized 块,获取 object 的锁。

  2. 打印 "t1 线程之前"

  3. 调用 object.wait()

    • 释放 object 的锁t1 进入等待状态。

  4. t2 先休眠 2 秒,确保 t1 先执行并进入 wait() 状态。

  5. t2 进入 synchronized 块,获取 object 的锁。

  6. 打印 "t2 线程之前"

  7. 调用 object.notify()

    • 唤醒 t1(WAITING -> BLOCKED),但 t1 不会立即执行,因为 t2 仍持有锁。

  8. 打印 "t2 线程之后",退出 synchronized 块,释放锁。

  9. t1 重新获取锁,继续执行 "t1 线程之后"

3. wait()、join()、sleep()方法的区别

方法sleep()join()wait()
所属类Thread类Thread类 Object类 
释放锁
唤醒条件 时间到期目标线程结束或超时notify()/notifyAll()或超时
使用限制 可以直接调用可以直接调用必须在同步块中使用 
 抛出InterruptedException
精度控制毫秒(实际精度依赖操作系统) 毫秒+纳秒  毫秒+纳秒 
线程状态变化RUNNING → WAITINGRUNNING → TIMED_WAITINGRUNNING → WAITING/TIMED_WAITING

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词