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

Java多线程如何将各线程计数正确写入txt文件避免内容覆盖

问题背景

基于继承Thread类实现自定义线程做CPU压力测试,共启动3个线程实例,需求是每小时将每个线程统计的小时迭代次数Hcount输出到txt文件,现有代码存在两个故障:

  • 最终文件中仅能看到单个线程的统计值
  • 每到下一小时触发写入时,会清空txt文件原有内容,仅保留最新写入的Hcount值,无法留存历史统计数据

原有问题代码如下:

import java.util.Random;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
public class MyThread extends Thread{
    public void run() {
        String B;//Will hold the value of Cpointer
        String A;//will hold the string value of Hcount
        Path fileName =
                Path.of("D:/EEoutput/Out.txt");
        Random rand = new Random();
        long Hcount = 0;//counts the number of iterations in an hour
        long t = System.currentTimeMillis();
        long end = t + 3800000*5;//a minute
        double a1 = 0; //random holder 1
        double a2 = 0;//random holder 2
        double answer = 0; // answer placeholder
        Long hour = System.currentTimeMillis()+3600000;//will be used to say when are we outputing the number of iterations to the file
        int Cpointer = 1;//will tell how many hours has passed
        while (System.currentTimeMillis() < end) {
            a1 = rand.nextDouble();
            a2 = rand.nextDouble();
            answer = a1 * 23 / (a2 + a1) + a2;
            Hcount++;
            if (System.currentTimeMillis() >= hour)// checks if the program needs to
            {
                 B = String.valueOf(Cpointer);
                 A=String.valueOf(Hcount);
                try {
                    Files.writeString(fileName, A);
                } catch (IOException e) {
                    e.printStackTrace();
                }
                hour = System.currentTimeMillis()+3600000;//sets stop to next hour
                Cpointer++;//declares that another hour has passed, will be used to tell how many iterations are there in a certain hour
                Hcount = 0;
            }
        }

    }
}
故障根因
  • 单线程值可见问题:3个线程并发操作同一个目标文件,未做任何写入同步控制;Files.writeString默认采用覆盖写入模式,线程触发写入的时间存在微小差,后执行写入的线程会直接冲掉其他线程刚写入的内容,最终文件仅保留最后一个完成写入操作的线程的数值。
  • 历史数据丢失问题:调用Files.writeString时未指定追加写入参数,方法默认会先截断清空文件全部原有内容,再写入新传入的内容,之前写入的所有历史记录会被直接清除。另外原代码仅写入Hcount数值,未标记线程ID、小时序号,即便写入成功也无法区分数据归属。
修复方案

修复点如下:

  • 新增全局静态锁对象,所有线程实例共用同一把锁,包裹文件写入逻辑,保证同一时刻仅一个线程能执行写操作,避免并发写入互相覆盖
  • 调用Files.writeString时传入打开选项:CREATE(文件不存在则自动创建)、APPEND(新内容追加到文件末尾,不截断原有内容)、WRITE(赋予写权限),解决历史数据被覆盖清空的问题
  • 写入内容补充线程ID、统计小时序号字段,每条记录末尾加换行符分隔,方便后续查看区分不同数据
  • 新增输出目录存在性判断,目录不存在时自动创建,避免首次运行因路径不存在抛出IO异常

修复后完整代码:

import java.util.Random;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;

public class MyThread extends Thread{
    // 全局静态锁,所有线程实例共用,保证写文件操作串行执行
    private static final Object FILE_WRITE_LOCK = new Object();
    private static final Path OUTPUT_DIR = Path.of("D:/EEoutput");
    private static final Path OUTPUT_FILE = OUTPUT_DIR.resolve("Out.txt");

    public void run() {
        Random rand = new Random();
        long Hcount = 0; // 小时级迭代计数
        long startRunTime = System.currentTimeMillis();
        long endRunTime = startRunTime + 3800000L *5; // 总运行时长
        double a1,a2,answer;
        long nextHourTrigger = System.currentTimeMillis() + 3600000; // 下一次写入统计值的时间戳
        int hourSeq = 1; // 已运行的小时序号

        // 预创建输出目录,避免路径不存在报错
        try {
            if (!Files.exists(OUTPUT_DIR)) {
                Files.createDirectories(OUTPUT_DIR);
            }
        } catch (IOException e) {
            e.printStackTrace();
            return;
        }

        while (System.currentTimeMillis() < endRunTime) {
            // CPU压力计算逻辑
            a1 = rand.nextDouble();
            a2 = rand.nextDouble();
            answer = a1 * 23 / (a2 + a1) + a2;
            Hcount++;

            // 到达小时统计节点
            if (System.currentTimeMillis() >= nextHourTrigger) {
                String record = String.format("线程ID:%d 第%d小时 迭代次数:%d%n",
                        this.getId(), hourSeq, Hcount);
                // 加锁执行写入,避免并发冲突
                synchronized (FILE_WRITE_LOCK) {
                    try {
                        // 追加模式写入,不覆盖原有内容
                        Files.writeString(OUTPUT_FILE, record,
                                StandardOpenOption.CREATE,
                                StandardOpenOption.WRITE,
                                StandardOpenOption.APPEND);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                // 重置下一小时触发时间、计数、小时序号
                nextHourTrigger = System.currentTimeMillis() + 3600000;
                hourSeq++;
                Hcount = 0;
            }
        }
    }

    // 启动测试示例
    public static void main(String[] args) {
        for (int i = 0; i < 3; i++) {
            new MyThread().start();
        }
    }
}

修复后运行,所有线程每小时的统计数据会按时间顺序追加写入到Out.txt文件中,不会覆盖历史内容,也不会出现线程间写入内容互相冲掉的问题。

内容的提问来源于stack exchange,提问作者Shaked Ben Eliezer

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 13:45:31