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
相关产品推荐
相关产品推荐

