当前位置: 首页 > news >正文

Java 多线程编程之 Object.wait 方法(工作原理、高级特性、notify 方法与 notifyAll 方法)

一、wait 方法

1、基本介绍
  1. wait 方法是 Java 中每个对象都拥有的方法,它继承自 Object 类

  2. wait 方法使当前线程进入等待状态,直到其他线程调用该对象的 notify 方法或 notifyAll 方法

  3. wait 方法必须在同步代码块中使用,否则抛出 InterruptedException 异常

public final void wait() throws InterruptedException

public final native void wait(long timeoutMillis) throws InterruptedException;

public final void wait(long timeoutMillis, int nanos) throws InterruptedException
2、基本使用
  1. 不带超时的 wait 方法
Object o = new Object();

new Thread(() -> {
    synchronized (o) {
        try {
            System.out.println("t1 进入等待状态");
            o.wait();
            System.out.println("t1 被唤醒后继续执行");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}).start();

new Thread(() -> {
    synchronized (o) {
        try {
            Thread.sleep(2000);
            System.out.println("t2 唤醒 t1");
            o.notify();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}).start();
# 输出结果

t1 进入等待状态
t2 唤醒 t1
t1 被唤醒后继续执行
  1. 带超时的 wait 方法
Object o = new Object();

new Thread(() -> {
    synchronized (o) {
        try {
            System.out.println("t1 进入等待状态");
            o.wait(2000);
            System.out.println("t1 被唤醒后继续执行");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}).start();
# 输出结果

t1 进入等待状态
t1 被唤醒后继续执行
  1. 必须在同步代码块中使用 wait 方法
Object o = new Object();

try {
    o.wait();
} catch (InterruptedException e) {
    e.printStackTrace();
}
# 输出结果

Exception in thread "main" java.lang.IllegalMonitorStateException: current thread is not owner
3、wait 方法对比 sleep 方法
特性wait 方法sleep 方法
所属类Object 类Thread 类
释放锁会释放锁不会释放锁
唤醒方式notify / notifyAll / 超时 / 中断超时 / 中断
使用场景线程间协作暂停

二、wait 方法工作原理

1、wait 方法与对象锁的关系
(1)基本介绍
  1. 调用 wait 方法前:必须持有对象锁

  2. 调用 wait 方法后:立即释放对象锁

  3. 被唤醒后:需要重新竞争对象锁,再继续执行

(2)演示
  1. 被唤醒后,还是需要重新竞争对象锁,再继续执行
Object o = new Object();

new Thread(() -> {
    synchronized (o) {
        try {
            System.out.println("t1 进入等待状态");
            o.wait();
            System.out.println("t1 被唤醒后继续执行");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}).start();

new Thread(() -> {
    synchronized (o) {
        try {
            Thread.sleep(2000);
            System.out.println("t2 唤醒 t1");
            o.notify();
            Thread.sleep(2000);
            System.out.println("t2 执行完毕");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}).start();
# 输出结果

t1 进入等待状态
t2 唤醒 t1
t2 执行完毕
t1 被唤醒后继续执行
  1. 超时后,还是需要重新竞争对象锁,再继续执行
Object o = new Object();

new Thread(() -> {
    synchronized (o) {
        try {
            System.out.println("t1 进入等待状态");
            o.wait(2000);
            System.out.println("t1 被唤醒后继续执行");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}).start();

new Thread(() -> {
    synchronized (o) {
        try {
            Thread.sleep(4000);
            System.out.println("t2 执行完毕");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}).start();
# 输出结果

t1 进入等待状态
t2 执行完毕
t1 被唤醒后继续执行
2、对象监视器模型
  • 每个 Java 对象都有一个关联的监视器(monitor),包含
  1. Owner Thread:当前持有对象锁的线程

  2. Entry Set:等待获取对象锁的线程集合

  3. Wait Set:调用了 wait 方法的线程集合

Object Monitor
Owner Thread
Entry Set
Wait Set
3、线程状态转换
  • 当调用 wait 方法方法时,线程状态会发生一系列变化
  1. 线程状态转换 RUNNABLE -> WAITING / TIMED_WAITING

  2. 释放对象锁

  3. 进入该对象锁的等待集合(Wait Set

  4. 当调用 notify 方法后,线程状态转换 WAITING -> BLOCKED(等待获取对象锁)

  5. 获取对象锁后,线程状态转换 BLOCKED -> RUNNABLE


三、wait 方法高级特性

1、虚假唤醒
  1. 虚假唤醒是指线程在没有收到 notify / notifyAll 的情况下从 wait 返回,虚假唤醒可能由以下原因引起

  2. 某些操作系统的线程调度机制可能导致 wait 提前返回,即使没有收到 notify / notifyAll

  3. JVM 实现可能在某些情况下允许虚假唤醒

  • 防御虚假唤醒的正确模式:wait 放在 while 语句中检查条件,而不是 if 语句,这样可以确保即使发生虚假唤醒,线程也会重新检查条件并继续等待
synchronized (【锁对象】) {
    while (【条件】) {
        【锁对象】.wait();
    }
    
    // 处理逻辑
}
2、中断处理
(1)基本介绍
  • wait 会响应中断,抛出 InterruptedException 异常,中断处理最佳策略是
  1. 捕获 InterruptedException 异常

  2. 恢复中断状态,即调用 interrupt 方法

(2)演示
Object o = new Object();

Thread t1 = new Thread(() -> {
    synchronized (o) {
        try {
            System.out.println("t1 进入等待状态");
            o.wait();
            System.out.println("t1 被唤醒后继续执行");
        } catch (InterruptedException e) {
            System.out.println("t1 被中断");
            System.out.println("t1 中断状态:" + Thread.currentThread().isInterrupted());
            Thread.currentThread().interrupt();
            System.out.println("t1 中断状态:" + Thread.currentThread().isInterrupted());
        }
    }
});

Thread t2 = new Thread(() -> {
    synchronized (o) {
        try {
            Thread.sleep(2000);
            System.out.println("t2 中断 t1");
            t1.interrupt();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
});

t1.start();
t2.start();
# 输出结果

t1 进入等待状态
t2 中断 t1
t1 被中断
t1 中断状态:false
t1 中断状态:true

四、notify 方法与 notifyAll 方法

1、基本介绍
  1. notify 方法随机唤醒等待集合(Wait Set)中的 1 个线程
public final native void notify()
  1. notifyAll 方法唤醒等待集合(Wait Set)中的所有线程
public final native void notifyAll()
  1. notify 方法与 notifyAll 方法必须在同步代码块中使用,否则抛出 InterruptedException 异常
2、演示
  1. notify 方法
Object o = new Object();

new Thread(() -> {
    synchronized (o) {
        try {
            System.out.println("t1 进入等待状态");
            o.wait();
            System.out.println("t1 被唤醒后继续执行");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}).start();

new Thread(() -> {
    synchronized (o) {
        try {
            System.out.println("t2 进入等待状态");
            o.wait();
            System.out.println("t2 被唤醒后继续执行");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}).start();

new Thread(() -> {
    synchronized (o) {
        try {
            Thread.sleep(2000);
            System.out.println("t3 随机唤醒 1 个线程");
            o.notify();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}).start();
# 输出结果

t1 进入等待状态
t2 进入等待状态
t3 随机唤醒 1 个线程
t1 被唤醒后继续执行
  1. notifyAll 方法
Object o = new Object();

new Thread(() -> {
    synchronized (o) {
        try {
            System.out.println("t1 进入等待状态");
            o.wait();
            System.out.println("t1 被唤醒后继续执行");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}).start();

new Thread(() -> {
    synchronized (o) {
        try {
            System.out.println("t2 进入等待状态");
            o.wait();
            System.out.println("t2 被唤醒后继续执行");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}).start();

new Thread(() -> {
    synchronized (o) {
        try {
            Thread.sleep(2000);
            System.out.println("t3 唤醒全部线程");
            o.notifyAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}).start();
# 输出结果

t1 进入等待状态
t2 进入等待状态
t3 唤醒全部线程
t1 被唤醒后继续执行
t2 被唤醒后继续执行
  1. notify 方法与 notifyAll 方法必须在同步代码块中使用
Object o = new Object();

new Thread(() -> {
    synchronized (o) {
        try {
            System.out.println("t1 进入等待状态");
            o.wait();
            System.out.println("t1 被唤醒后继续执行");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}).start();

try {
    Thread.sleep(1000);
} catch (InterruptedException e) {
    e.printStackTrace();
}

o.notify();
# 输出结果

t1 进入等待状态
Exception in thread "main" java.lang.IllegalMonitorStateException: current thread is not owner
Object o = new Object();

new Thread(() -> {
    synchronized (o) {
        try {
            System.out.println("t1 进入等待状态");
            o.wait();
            System.out.println("t1 被唤醒后继续执行");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}).start();

try {
    Thread.sleep(1000);
} catch (InterruptedException e) {
    e.printStackTrace();
}

o.notifyAll();
# 输出结果

t1 进入等待状态
Exception in thread "main" java.lang.IllegalMonitorStateException: current thread is not owner

相关文章:

  • MySQL基础与核心操作
  • ArkUI之常见基本布局(下)
  • 【ISP】HDR算法
  • AWS Lambda 集成更新详解:打造无缝云函数体验
  • Vuex状态管理
  • socket系统调用的参数涵义
  • 卡尔曼滤波入门(二)
  • Python之文件操作详解
  • Python FastApi(7):请求体
  • 在win11 环境下 新安装 WSL ubuntu + 换国内镜像源 + ssh + 桌面环境 + Pyhton 环境 + vim 设置插件安装
  • 私有化部署dify + DeepSeek-R1-Distill-Qwen-32B + bge-m3
  • Razer macOS v0.4.10快速安装
  • 【21期获取股票数据API接口】如何用Python、Java等五种主流语言实例演示获取股票行情api接口之沪深A股阶段主力动向数据及接口API说明文档
  • 【Linux】System V信号量与IPC资源管理简易讲解
  • Dubbo 通信流程 - 服务的调用
  • TCP可靠传输与慢启动机制
  • 项目上传github——SSH连接配置文档
  • 无参数读文件RCE
  • STRUCTBERT:将语言结构融入预训练以提升深度语言理解
  • AWS Aurora存算分离架构
  • 怀化找什么人做网站/品牌seo如何优化
  • .net企业网站/怎么制作自己公司网站
  • 5173游戏交易网站源码/网站优化推广费用
  • wordpress显示用户列表/seo综合优化公司
  • 做淘宝店铺装修的公司网站/南昌百度网站快速排名
  • 网上注册公司要多少钱/提高seo关键词排名