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
相关产品推荐
相关产品推荐

