wait/notify调度线程执行顺序

想必我们都知道线程的执行的顺序是抢占式执行,有时候我们需要线程执行的顺序按照一定的要求执行,也就是说有没有一种方法可以控制线程的执行顺序呢~

答案是有的,就是wait/notify

wait是Object里的方法,通过调用object对象来调用wait方法,在执行的过程中wait方法做了三件事:

1.释放当前锁

2.使当前锁等待通知

3.当其他线程调用notify,并且释放锁之后,重新拿到锁,继续执行剩下的代码

f403211ef99c456f9dd053f51666869c.png

注意:t2直接调用加锁会发生锁竞争,会进行阻塞,只有t1调用了wait释放锁才能真正意义上拿到锁

t2调用notify,t1也不会马上就能拿到锁,会阻塞一段时间,只有当t2的程序执行完毕,把锁释放出来,t1才能重新进行抢占式执行,重新拿到锁,执行下面的程序

说到这里,t1调用wait首先先释放锁,那么肯定是要先用synchronied上锁,然后再释放锁给其他线程.要保证加锁的对象和调用wait对象得是同一个对象.还要保证,调用wait的对象和调用notify的对象也是同一个对象.

notity方法一次只能唤醒一个wait,也就是说当多个线程同时执行了wait方法,notify只能唤醒一个,且是随机的.当然Java也提供了notifyAll方法,是指唤醒所有的wait.但是如果这些线程同时被唤醒,他们会重新竞争锁,最后仍然是串行执行.

下面给出一个wait/notity的实际例子:

public class Demo2 {
    public static void main(String[] args) {
        //保证等待和通知
<think>好的,用户想了解Java中Object类的waitnotify方法的底层实现机制,包括调用链和JVM源码分析。我需要结合提供的引用内容,整理出一个清晰的回答。 首,根据引用1,waitnotify是Object类的方法,每个Java对象都有。接着引用3提到,在JVM源码中,notifynotifyAll不会立即释放ObjectMonitor,而是在执行monitorexit指令时才释放。引用4和5指出底层使用了park/unpark机制,在Linux下通过pthread_cond实现,线程wait时会放弃CPU。引用5还提到了ObjectMonitor的exit方法。 需要分步骤解释:首Java层的方法调用如何进入JVM,然后JVM如何处理,涉及ObjectMonitor对象,以及线程状态的变化。还要说明锁的释放时机,可能存在的竞争问题,比如为什么要在同步块中使用这些方法。引用2中的代码示例展示了同步块的使用,但用户可能想知道底层如何保证同步。 需要组织信息,从Java API到JVM源码,再到操作系统层的实现。可能的结构是:Java层调用 -> JVM入口 -> ObjectMonitor机制 -> 线程挂起与唤醒 -> 系统调用。同时要结合引用中的各个点,比如park/unpark和pthread_cond的关系,以及monitorexit的重要性。 还需要注意用户可能对性能感兴趣,如引用4提到的线程放弃CPU,这对系统性能的影响。此外,引用3和5提到的entry set和ObjectWaiter节点,可以解释线程如何被管理。 最后生成相关问题时,要涵盖底层机制、使用注意事项、性能影响和与其他线程工具的比较。</think>### Java Object类wait/notify方法底层实现机制 #### 1. 核心调用链路 Java层调用 `wait()`/`notify()` 方法时,会通过以下路径进入JVM实现: ``` Java API -> JVM_ENTRY(jvm.cpp) -> ObjectMonitor::wait()/notify() ``` 具体过程: - **Java层调用**:`synchronized(obj) { obj.wait(); }` 触发`monitorenter`指令 - **JVM入口**:`jvm.cpp`中的`JVM_MonitorWait`和`JVM_MonitorNotify`方法 - **对象监视器**:通过`ObjectMonitor`对象管理线程状态(每个Java对象关联一个ObjectMonitor)[^3][^5] #### 2. ObjectMonitor关键结构 ```cpp class ObjectMonitor { volatile markOop _header; // 对象头 void* volatile _owner; // 持有锁的线程 ObjectWaiter* _WaitSet; // 等待队列(wait线程) ObjectWaiter* _EntryList; // 阻塞队列(等待锁的线程) // ... } ``` - `_WaitSet`存储处于WAITING状态的线程(通过wait()进入) - `_EntryList`存储等待获取锁的BLOCKED线程 #### 3. wait()实现步骤 ```cpp void ObjectMonitor::wait(jlong millis, bool interruptible) { // 1. 构造ObjectWaiter节点 ObjectWaiter node(Self); // 2. 将线程加入_WaitSet队列 AddWaiter(&node); // 3. 释放锁并触发monitorexit exit(true, Self); // 4. 调用park挂起线程(底层通过pthread_cond_wait实现) park(millis); // 5. 被唤醒后重新竞争锁 enter(Self); } ``` 关键点: - 调用`wait()`会立即释放对象锁(通过`exit()`触发monitorexit) - 线程挂起通过操作系统级条件变量实现(Linux下`pthread_cond_wait`)[^4] #### 4. notify()实现步骤 ```cpp void ObjectMonitor::notify(TRAPS) { // 1. 从_WaitSet移出第一个等待线程 ObjectWaiter* iterator = DequeueWaiter(); // 2. 将线程转移到_EntryList或直接唤醒 if (Policy == 0) { // 默认策略:移入EntryList enqueue(iterator); } else { // 策略1:直接唤醒 unpark(iterator->_thread); } } ``` 重要特性: - `notify()`不会立即释放锁,实际锁释放发生在同步块结束的`monitorexit`指令 - 被唤醒的线程需要重新竞争锁才能继续执行[^5] #### 5. 操作系统层实现 - **挂起**:通过`pthread_cond_wait()`让出CPU,线程进入WAITING状态 - **唤醒**:`pthread_cond_signal()`触发线程调度 - **性能优势**:挂起的线程不占用CPU时间片[^4] #### 6. 典型问题案例 引用[2]中的代码存在严重问题: ```java synchronized (flag) { flag = "false"; // 修改锁对象引用导致后续notify失效 flag.notify(); } ``` 当修改`flag`引用后,其他线程的`synchronized(flag)`可能锁定不同对象,导致`notify()`失效。这是典型的对象锁引用逃逸问题。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值