并发测试中线程无限运行及totalBytes未更新问题求助
线程同步问题排查与修复
说明事项
- 使用JetBrains IntelliJ IDEA社区版
- 代码包含3个简短类,易于阅读
- 原版代码可正常运行并得到预期结果
预期结果
thread1先更新totalBytes,随后thread2打印结果。
实际结果
thread1执行完成,但thread2无限运行,且未读取到totalBytes的更新值。
问题点
- 核心错误:DownloadFileTask类的构造方法未使用传入的status对象,而是新建了一个实例,导致thread1和thread2操作的是完全独立的DownloadStatus对象,彼此状态互不可见
- 原怀疑的
synchronized未同步isDone访问不成立,因为isDone()和done()都是同步方法,但实例隔离导致thread2永远看不到thread1设置的isDone值
代码展示
1. DownloadStatus类
public class DownloadStatus { private boolean isDone; private int totalBytes; private int totalFiles; private final Object totalBytesLock = new Object(); private final Object totalFilesLock = new Object(); public void incrementTotalBytes(){ synchronized (totalBytesLock){ totalBytes++;}} public void incrementTotalFiles(){ synchronized (totalFilesLock){ totalFiles++;}} public int getTotalBytes(){ return totalBytes;} public int getTotalFiles(){ return totalFiles;} public synchronized boolean isDone(){ return isDone;} public synchronized void done(){ isDone = true;} }
2. DownloadFileTask类(存在错误)
public class DownloadFileTask implements Runnable { private DownloadStatus status; public DownloadFileTask(DownloadStatus status) { this.status = new DownloadStatus();} // 此处错误:应使用传入的status而非新建实例 @Override public void run () { System.out.println("Downloading a File: " + Thread.currentThread().getName()); for (var i = 0; i < 1_000_000; i++) { if (Thread.currentThread().isInterrupted()) return; status.incrementTotalBytes();} status.done(); System.out.println(); System.out.println("Download Complete: " + Thread.currentThread().getName());} }// End of DownloadFileTask
3. ThreadDemo类(含main方法)
public class ThreadDemo { public static void show() { var status = new DownloadStatus(); var thread1 = new Thread(new DownloadFileTask(status)); var thread2 = new Thread(() -> { System.out.println("Thread: " + Thread.currentThread().getName() + " has started."); while (!status.isDone()) {} System.out.println(Thread.currentThread().getName() + " has finished. Total Bytes: "+ status.getTotalBytes()); }); thread1.start(); thread2.start(); } public static void main(String[] args){ show(); } }// End of ThreadDemo
已尝试无效方案
- 将SDK切换为视频课程使用的OpenJDK-12.0.1,匹配对应语言级别
- 完全复制原版代码的字面内容与语法结构
- 添加status.isDone()调用,尝试确认done()方法是否更新isDone字段
- 将synchronized块改为同步incrementTotalBytes()、incrementTotalFiles()、isDone()、done()方法,未同步getTotalBytes()方法
疑问解答
thread1何时发送中断信号?
当前代码中没有任何地方调用thread1.interrupt(),所以if (Thread.currentThread().isInterrupted()) return;这个判断永远不会触发,属于预留的中断处理逻辑,当前场景下用不到。若isDone被设为true,while循环何时退出?
因为thread1和thread2操作的是不同的DownloadStatus实例,thread1设置的isDone对thread2完全不可见,所以循环永远不会退出。
修复方案
只需修改DownloadFileTask的构造方法,使用传入的status对象:
public DownloadFileTask(DownloadStatus status) { this.status = status; // 替换原来的new DownloadStatus() }
修复后,两个线程共享同一个状态实例,thread1调用done()后,thread2能读取到更新后的isDone值,循环会正常退出并打印正确的totalBytes。
内容的提问来源于stack exchange,提问作者Coding Noob Unfiltered
相关产品推荐
相关产品推荐

