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

Java异步线程编辑文件时多线程并发写入导致文件损坏问题咨询

问题核心诱因分析
  • 线程可见性问题:editThreadStarted 变量未加volatile修饰,多线程并发调用时可能出现可见性异常,导致同时创建多个编辑线程,多个线程同时操作文件触发冲突。
  • 集合线程不安全:使用普通HashMap存储队列,多线程同时执行put、get、remove操作时会出现脏数据、丢更新甚至触发集合死循环,直接导致队列逻辑失效。
  • 竞态条件漏洞:判断editThreadStarted和启动线程的操作不是原子操作,即使变量可见,也可能出现两个线程同时通过判断、先后启动两个编辑线程的情况,引发文件操作冲突。
信号量方案实现(符合你的需求,可直接替换原有逻辑)

你提到的信号量方案确实是当前场景下最简单可靠的方案,许可数设为1即可实现同一时间只有一个线程执行文件写入逻辑,完全避免并发冲突,也不需要自己维护队列:
首先在类中声明信号量:

// 许可数为1,开启公平模式避免写入请求饥饿
private final Semaphore writeSemaphore = new Semaphore(1, true);

然后替换原有editParameter逻辑:

public final boolean editParameter(String key, String value) {
    try {
        // 申请写入许可,申请不到会阻塞等待
        writeSemaphore.acquire();
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
        DemiConsole.error("Write request interrupted for key: " + key);
        handleTrace(e);
        return false;
    }

    File inputFile = getFile();
    File tempFile = new File(inputFile.getParent(), inputFile.getName() + ".temp");

    try {
        if (!tempFile.exists()) {
            tempFile.createNewFile();
        }

        // try-with-resources会自动关闭流,不需要手动close
        try (BufferedReader reader = new BufferedReader(new FileReader(inputFile));
             BufferedWriter writer = new BufferedWriter(new FileWriter(tempFile))) {
            String currentLine;
            while ((currentLine = reader.readLine()) != null) {
                String trimmedLine = currentLine.trim();
                if (trimmedLine.startsWith(key)) {
                    writer.write(key + ":" + value + System.lineSeparator());
                    continue;
                }
                writer.write(currentLine + System.lineSeparator());
            }
        } catch (IOException e) {
            DemiConsole.error("IO exception when editing key ("+key+") in file ("+inputFile.getName()+")");
            handleTrace(e);
            tempFile.delete();
            return false;
        }

        // 文件替换操作要判断返回值,失败则回滚
        boolean deleteSuccess = inputFile.delete();
        if (!deleteSuccess) {
            tempFile.delete();
            DemiConsole.error("Failed to delete original file");
            return false;
        }
        boolean renameSuccess = tempFile.renameTo(inputFile);
        if (!renameSuccess) {
            DemiConsole.error("Failed to rename temp file to original file");
            return false;
        }
        return true;
    } finally {
        // 无论操作成功失败都释放许可,避免死锁
        writeSemaphore.release();
    }
}
额外优化建议
  • 如果后续写入并发很高,可以保留队列逻辑,改用ConcurrentLinkedQueue存储待写入任务,配合单线程线程池Executors.newSingleThreadExecutor()消费队列,比自己实现的线程调度逻辑更稳定。
  • 可以定期对文件做备份,避免极端情况下文件损坏丢失数据。

内容的提问来源于stack exchange,提问作者Stormer

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.03 21:57:01