You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java中wait()与notify()无法正常工作的问题求助

问题原因分析与解决方案

让我来帮你拆解这个问题,移除thread.sleep()后功能失效的核心原因有两个,咱们逐个说清楚:

1. Scanner阻塞时Operator持续持有锁

你的Operator线程里,scanner.nextLine()是写在synchronized (strings)同步块内部的!这个方法是阻塞式的——它会一直等待你在控制台输入内容,直到按下回车才会继续执行。

当你移除同步块外的sleep(1000)后,流程变成了这样:

  • Operator进入同步块,调用scanner.nextLine(),此时它一直攥着strings的锁,等你输入内容;
  • 你输入完成后,Operator添加元素并调用notify(),随后退出同步块释放锁;
  • 但紧接着Operator立刻进入下一次循环,再次抢到strings的锁,又调用scanner.nextLine()开始等待下一次输入;
  • 而Machine线程被notify()唤醒后,需要获取strings的锁才能执行移除和打印操作,但锁已经被Operator重新拿走了,导致Machine一直阻塞在同步块外,根本没机会运行代码。

原来有sleep(1000)的时候,Operator退出同步块后会休眠1秒,这段时间锁是释放的,Machine有足够的时间获取锁、完成打印逻辑。

2. Machine线程的循环逻辑存在竞态条件

你的Machine线程用while (strings.isEmpty())作为循环条件,但这个判断不在同步块内部,会引发竞态条件:

  • 当Machine被唤醒并移除元素后,strings变回空,退出同步块;
  • 如果此时Operator刚好快速添加了新元素(虽然这里因为Scanner阻塞不太容易发生,但逻辑上存在漏洞),strings.isEmpty()会返回false,Machine就会直接退出整个循环,再也不会处理后续元素了。

修正后的代码

把scanner.nextLine()移到同步块外面,同时把Machine的空判断放到同步块内部(改用经典的while等待模式),就能彻底解决问题:

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Scanner;

public class WaitAndNotify {
    private static List<String> strings = Collections.synchronizedList(new ArrayList<>());

    public static void main(String[] args) {
        new Operator().start();
        new Machine().start();
    }

    // Operator线程
    static class Operator extends Thread {
        @Override
        public void run() {
            Scanner scanner = new Scanner(System.in);
            while (true) {
                System.out.println("Operator is working");
                // 把输入操作移到同步块外,避免持有锁时阻塞
                String input = scanner.nextLine();
                synchronized (strings) {
                    strings.add(input);
                    strings.notify();
                    System.out.println("Notifying to machine");
                }
                // 移除sleep也能正常工作
            }
        }
    }

    // Machine线程
    static class Machine extends Thread {
        @Override
        public void run() {
            while (true) { // 用无限循环保证持续处理
                synchronized (strings) {
                    // 空判断放到同步块内,避免竞态条件
                    while (strings.isEmpty()) {
                        try {
                            System.out.println("Machine is waiting");
                            strings.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    System.out.println(strings.remove(0));
                }
            }
        }
    }
}

修改后,不管有没有sleep(1000),功能都能稳定运行:

  • Operator获取输入时不再持有锁,Machine被唤醒后能立刻拿到锁执行打印;
  • Machine用内部的while (strings.isEmpty())判断,彻底避免了竞态条件,保证只要队列空就等待,有元素就处理。

内容的提问来源于stack exchange,提问作者Hayk Mkrtchyan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 08:08:05