Java多线程服务器文件实时追加功能异常:运行时无写入,仅调试模式部分生效的解决方案咨询
解决多线程服务器文件写入失效的问题
嘿,我仔细啃了你的代码和问题描述,很快就揪出了几个导致文件写入异常的核心问题,咱们一步步来修复:
核心问题拆解
- BufferedWriter被提前关闭:你的代码在第一次写入后就调用了
bfw.close(),这直接把文件流给关死了!后续循环再尝试写入自然完全无效——这就是正常运行时文件空无一物的根本原因。调试时能写入是因为断点打乱了执行顺序,让你误以为能正常工作,但实际上close之后流已经废了。 - 非线程安全的队列操作:
ArrayList本身不是线程安全的,服务器线程在往队列加Command,DataBase线程在读队列的size和取元素,并发场景下很容易出现竞态条件,比如刚判断完lastSize < cmdQueue.size(),队列大小就被修改,导致元素读取出错或漏处理。 - 消息处理逻辑有漏洞:你只处理队列的最后一个元素,如果服务器连续发多条消息,中间的元素会直接被漏掉;而且初始的
lastSize设置逻辑混乱(先设为队列当前size又立刻改成0),很容易导致重复或遗漏处理。
修改后的完整代码实现
1. 修复DataBase类的run方法
import java.io.BufferedWriter; import java.io.FileWriter; import java.io.IOException; import java.util.ArrayList; import java.util.concurrent.locks.ReentrantLock; public class DataBase implements Runnable { private final ArrayList<Command> cmdQueue; // 用锁保证队列操作的线程安全 private final ReentrantLock queueLock = new ReentrantLock(); // 记录最后处理到的元素索引,比用size更可靠 private int lastProcessedIndex = 0; // 构造时直接传入队列,避免额外的set操作 public DataBase(ArrayList<Command> cmdQueue) { this.cmdQueue = cmdQueue; } @Override public void run() { // 用try-with-resources自动管理流的生命周期,避免手动关闭出错 try (FileWriter fw = new FileWriter(BlackBox.createBlackBox(), true); BufferedWriter bfw = new BufferedWriter(fw)) { // 响应线程中断,支持优雅停止 while (!Thread.currentThread().isInterrupted()) { queueLock.lock(); try { // 遍历所有未处理的新元素 while (lastProcessedIndex < cmdQueue.size()) { Command cmd = cmdQueue.get(lastProcessedIndex); bfw.write(cmd.getCommandString()); bfw.newLine(); // 可选,让每条消息单独占一行,方便查看 bfw.flush(); // 强制把缓冲区内容写入文件,确保即时性 lastProcessedIndex++; } } finally { // 无论是否出错,都要释放锁 queueLock.unlock(); } // 短暂休眠,避免空转占用CPU资源 try { Thread.sleep(10); } catch (InterruptedException e) { Thread.currentThread().interrupt(); break; } } } catch (IOException ioe) { System.err.println("文件写入异常: " + ioe.getMessage()); ioe.printStackTrace(); } } }
2. 简化线程启动代码
// 直接通过构造方法传入队列,不需要额外的set操作 DataBase dBase = new DataBase(server.cmdQueue); Thread bBoxThread = new Thread(dBase); bBoxThread.start();
关键修改说明
- 流的生命周期管理:用try-with-resources语法创建流,确保线程结束时流会自动关闭,彻底解决提前关闭流的问题。
- 线程安全保障:添加
ReentrantLock同步队列操作,避免多线程下的竞态条件;如果你的场景中读多写少,也可以直接用CopyOnWriteArrayList替代ArrayList,锁的方式在频繁写入时性能更优。 - 消息处理逻辑优化:用
lastProcessedIndex记录最后处理的元素索引,确保每个Command都被逐个处理,不会漏掉任何一条消息。 - 即时写入保证:每次写入后调用
bfw.flush(),确保消息立刻写入文件,解决调试时最后一条消息无法写入的问题。 - 避免CPU空转:添加短暂休眠,让DataBase线程在没有新消息时释放CPU资源,减少不必要的性能消耗。
内容的提问来源于stack exchange,提问作者petrunko
相关产品推荐
相关产品推荐

