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

并发测试中线程无限运行及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()方法

疑问解答

  1. thread1何时发送中断信号?
    当前代码中没有任何地方调用thread1.interrupt(),所以if (Thread.currentThread().isInterrupted()) return;这个判断永远不会触发,属于预留的中断处理逻辑,当前场景下用不到。

  2. 若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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 13:42:53