Java多线程的暗号密码:5分钟掌握wait/notify

wait和join的区别

wait和join在使用上都是等待。

但是join是等待其他线程结束,而wait是等待其他线程的notify通知再运行。

当拿到锁的线程,发现要执行的任务时机不成熟的时候,使用wait进行阻塞等待,然后等时机成熟了再notify通知他可以接着走了。

这样的使用可以大大约束线程的运行顺序,此外他还有以下优点:

优势 说明
节省 CPU 替代忙等待,线程挂起时不消耗资源。
精准唤醒 通过 notify 按条件触发,避免无效轮询。
线程安全 自动释放/重新获取锁,避免死锁和竞态条件。
轻量级 无需额外库,内置 synchronized 即可实现。

使用方法与注意事项

wait和notify是Object的方法,任意一个类都可以使用。

java 复制代码
Object.wait();

调用 wait() 前必须获得对象的监视器锁(即 synchronized),这里需要保证对象是同一个。

java 复制代码
synchronized(locker) {
	locker.wait();
}

在执行wait操作的时候是必须要加上锁的,代码进入 wait,就会先释放锁,并且阻塞等待如果其他线程做完了必要的工作,调用 notify 唤醒这个 wait 线程wait 就会解除阻塞, 重新获取到锁. 继续执行并返回

java 复制代码
synchronized(locker) {
	//加锁状态
	locker.wait();//临时解锁
	//再加锁
}

在执行notify的时候虽然不涉及加锁操作,但是java强制要求搭配synchronized来使用

java 复制代码
  Thread t2 = new Thread(()->{
     synchronized (locker1) {
     locker1.notify();
	}
 });

而在执行notify的时候也要保证这两个是同一个对象,这个共同的对象就是他俩沟通的桥梁,如果是两个不同的对象,则没有任何作用。

java 复制代码
 Thread t1 = new Thread(()->{
     synchronized (locker1) {
     locker1.wait();
	}
 });
 
  Thread t2 = new Thread(()->{
     synchronized (locker2) {
     locker2.notify();
     //对于loker1的wait不会起到任何作用
	}
 });

如果有多个线程处于这个锁的wait等待状态,notify会随机唤醒一个线程,notify一次只会唤醒一个线程,nootifyAll方法可以唤醒所有线程。

java 复制代码
  Thread t1 = new Thread(()->{
     synchronized (locker1) {
     locker1.notifyAll();
     //唤醒所有loker1的线程
	}
 });

wait方法不传入参数时处于"死等"模式,如果传入时间,过了这个时间就不再等待,和join方法类似。

java 复制代码
 Thread t1 = new Thread(()->{
     synchronized (locker1) {
     locker1.wait(1000);
      //等待1s
	}
 });

务必要确保, 先 wait,后 notify,才有作用如果是先 notify, 后 wait, 此时 wait 无法被唤醒.notify 的这个线程,也没有副作用(notify 一个没有在 wait 的对象,不会报错)

小练习~

题目:使用两个线程交替打印 1~100 的数字,要求严格按顺序输出(线程A打印1,线程B打印2,线程A打印3...)。

🎯 核心要求

线程A负责打印奇数,线程B负责打印偶数。

必须使用 wait() 和 notify() 实现线程间协作,不能用 sleep() 或忙等待。

输出结果必须严格按顺序:1, 2, 3, 4,..., 100。

预期输出:

java 复制代码
Thread-A: 1  
Thread-B: 2  
Thread-A: 3  
Thread-B: 4  
...  
Thread-A: 99  
Thread-B: 100

答案:

java 复制代码
public class AlternatePrint {
    private static final Object lock = new Object();
    private static int num = 1;
    private static final int MAX = 100;

    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            synchronized (lock) {
                while (num <= MAX) {
                    if (num % 2 == 1) {
                        System.out.println("Thread-A: " + num++);
                        lock.notify();
                    } else {
                        try { lock.wait(); }
                        catch (InterruptedException e) { e.printStackTrace(); }
                    }
                }
            }
        });

       Thread t2 = new Thread(() -> {
            synchronized (lock) {
                while (num <= MAX) {
                    if (num % 2 == 0) {
                        System.out.println("Thread-B: " + num++);
                        lock.notify();
                    } else {
                        try { lock.wait(); }
                        catch (InterruptedException e) { e.printStackTrace(); }
                    }
                }
            }
        });
        t1.start();
        t2.start();
    }
}
相关推荐
代码不停2 分钟前
子数组问题
java·算法
geovindu10 分钟前
CSharp: State Pattern
开发语言·后端·c#·.net·状态模式·行为模式
IT小白杨14 分钟前
TikTok小店防封号浏览器实战:2026年多店铺环境隔离与账号安全运营技术解析
开发语言·数据库·安全·php·chrome devtools·指纹浏览器
信誓旦旦的程序猿17 分钟前
【零依赖量化数据实战 #25】北交所技术指标与基本面
java·人工智能·python·股票数据api·股票数据·股票数据api接口·股票api数据接口
2601_9669496520 分钟前
Python 量化开发为什么适合使用金融数据 SDK?从数据获取到策略研究的工程化实践
开发语言·python·数据分析·量化交易·股票数据·quantdash
万年咸鱼21 分钟前
Java ArrayList 详解:原理、用法与实战
java·开发语言·windows
长友cy26 分钟前
Qt官方示例Application源码阅读
开发语言·qt
程序员良辰33 分钟前
服务器 JDK 环境变量配置:一键安装和手动配置有什么区别?
开发语言·python
奈斯先生Vector40 分钟前
本地图片识别怎么接入多模态 AI?用 Python API 理解 GPT-4o Vision 的真实工作流
开发语言·人工智能·windows·python·网络协议·http·aigc
OPEN-F44 分钟前
C++17新特性精讲:结构化绑定、if constexpr与实用类型
开发语言·c++