Java多线程使用Wait/Notify与synchronized出现死锁无法运行问题求解
Java多线程wait/notify协作死锁问题修复方案
核心问题排查
原代码死锁及功能异常的根本原因如下:
- 两个线程进入同步块后第一行均调用
wait(),没有任何线程优先执行业务逻辑触发唤醒动作,双方永久等待直接死锁 - 空格统计逻辑错误:原代码通过
sc.next()计数得到的是单词数量,不是空格数量 - 状态位
flag逻辑完全倒置,未正确标记「空格统计完成」的状态 - 单词修改逻辑存在边界缺陷:单字母单词会触发字符串索引越界异常
修复后完整代码
import java.io.File; import java.io.PrintWriter; import java.util.Scanner; public class Dispatcher { public static void main(String[] args) { FileHandler fileHandler = new FileHandler(); Thread countThread = new CountSpaces(new File("text.txt"), fileHandler); Thread changeThread = new ChangeText(new File("text.txt"), fileHandler); countThread.start(); changeThread.start(); } } class CountSpaces extends Thread{ File file; FileHandler fileHandler; public CountSpaces(File file, FileHandler fileHandler) { this.file = file; this.fileHandler = fileHandler; } @Override public void run() { fileHandler.countSpace(file); } } class ChangeText extends Thread { File file; FileHandler fileHandler; public ChangeText(File file, FileHandler fileHandler) { this.file = file; this.fileHandler = fileHandler; } @Override public void run() { try { fileHandler.changeLetters(file); } catch (InterruptedException e) { e.printStackTrace(); } } } class FileHandler { // 标记空格统计是否完成 boolean countFinished = false; int spaceCount; void countSpace(File file) { try (Scanner sc = new Scanner(file)) { // 直接读整个文件内容统计空格,避免漏算首尾/连续空格 StringBuilder content = new StringBuilder(); while (sc.hasNextLine()) { content.append(sc.nextLine()).append("\n"); } int count = 0; for (char c : content.toString().toCharArray()) { if (c == ' ') { count++; } } synchronized (this){ this.spaceCount = count; countFinished = true; // 统计完成唤醒修改线程 notify(); } } catch (Exception e) { e.printStackTrace(); } } void changeLetters(File file) throws InterruptedException { synchronized (this){ // 循环等待防止虚假唤醒,直到统计完成 while (!countFinished) { wait(); } // 重新读文件做修改 try (Scanner sc = new Scanner(file); PrintWriter printWriter = new PrintWriter(file + "result")) { if (spaceCount % 2 == 0) { // 偶数:首字母大写 while (sc.hasNext()) { String word = sc.next(); if (word.length() == 1) { printWriter.print(word.toUpperCase()); } else { printWriter.print(word.substring(0, 1).toUpperCase() + word.substring(1)); } // 保留原空格分隔,需完全保留原格式可直接读全字符串替换 printWriter.print(" "); } } else { // 奇数:尾字母大写 while (sc.hasNext()) { String word = sc.next(); if (word.length() == 1) { printWriter.print(word.toUpperCase()); } else { printWriter.print(word.substring(0, word.length() - 1) + word.substring(word.length() - 1).toUpperCase()); } printWriter.print(" "); } } } catch (Exception e) { e.printStackTrace(); } } } }
关键调整说明
- 调整执行顺序:统计线程优先执行空格计数,完成后再唤醒修改线程,从根源避免双向等待死锁
wait()放在循环中判断状态位,符合Java多线程规范,避免虚假唤醒导致的逻辑异常- 修复空格统计逻辑,直接遍历全文本字符统计空格,兼容首尾空格、连续空格场景
- 补充单字母单词的边界处理,避免索引越界
内容的提问来源于stack exchange,提问作者user17320675
相关产品推荐
相关产品推荐

