seek后write不生效主因是缺乏原子性、未强制刷盘(需getFD().sync())及多进程竞争,且Windows下外部程序占用文件句柄会导致锁失效。

seek 之后 write 不生效?因为没刷新缓冲区也没处理文件指针偏移
RandomAccessFile 的 seek() 定位到文件末尾再 write(),看似能追加,但多进程下大概率写乱——根本原因是:没有原子性保证,且 seek() 和 write() 是两步操作,中间可能被其他进程插入。更隐蔽的问题是:write() 调用后数据可能滞留在 JVM 缓冲区(尤其使用 writeBytes() 或 writeUTF() 时),而 RandomAccessFile 本身不提供 flush()(它不是 Writer),必须靠 getFD().sync() 强制刷盘,否则日志可能丢失。
实操建议:
- 每次写入前先用
length()获取当前文件大小,再seek(length()),别依赖seek(0, SEEK_END)风格(RandomAccessFile没这语法) - 写完立即调用
getFD().sync(),不要省略——这是保证落盘的关键动作 - 避免在
seek()后反复write()多次再 sync;应把整条日志拼成一个byte[]一次性write(),减少 IO 中断窗口
文件锁(FileLock)必须用非阻塞 + try-with-resources 才可靠
RandomAccessFile 的 getChannel().tryLock() 是唯一能跨进程互斥的机制,但默认的 lock() 是阻塞式,在多进程争抢时会导致死等甚至 JVM 挂起。而且锁对象生命周期必须严格绑定到写入动作——锁太早获取、太晚释放,或者异常未释放,都会卡死其他进程。
实操建议:
- 永远用
channel.tryLock(0, Long.MAX_VALUE, true)(非阻塞、独占、覆盖整个文件);如果返回null,说明锁被占,应退避重试或丢弃/缓冲日志,不能硬等 - 锁对象必须放在
try块内,并在finally中显式lock.release();推荐用 try-with-resources 包裹自定义AutoCloseable锁包装类 - 不要对同一
RandomAccessFile实例复用锁;每次写都新建 RAFF + channel + tryLock 流程,避免状态污染
Windows 下 lock() 失效?那是没关掉记事本/IDE 文件监控
Windows 对文件锁限制极严:只要任意进程以可读方式打开过该文件(比如记事本预览、IDE 实时解析、杀毒软件扫描),tryLock() 就会直接失败并抛 IOException: The process cannot access the file。这不是代码 bug,是系统级限制。
将 WCAG 2 AA 无障碍标准应用于设计系统、UI 组件和网页——通过可运行的验证器进行对比度计算,采用设计 token 模式使颜色天生可访问,设定排版和焦点状态规则,并提供语义检查清单。适用于构建或审查 UI。
实操建议:
- 生产环境禁用所有第三方工具对日志文件的实时访问;部署脚本中加入
handle.exe -a your.log(Sysinternals)排查谁在 hold 句柄 - Linux/macOS 更稳妥,但也要注意:NFS 挂载点上的
FileLock不跨服务器,此时需退回到基于文件原子重命名(如log.tmp → log)的方案 - 若必须兼容 Windows 且无法控制外部程序,改用
java.nio.channels.FileChannel.lock()配合StandardOpenOption.APPEND(但此时seek()失效,只能靠 OS 级 append 语义)
seek + lock 组合仍可能丢日志?因为没处理进程意外退出
即使锁和刷盘都做了,如果进程在 write() 完成但 sync() 前崩溃(如 OOM、kill -9),这部分日志就只在页缓存里,不会落盘。而 FileLock 在进程退出时自动释放,其他进程会立刻续写,导致这段“半截日志”永远丢失,且后续内容错位。
实操建议:
- 关键业务日志必须启用
getFD().sync(),但要接受性能代价;若吞吐压不住,可折中:每 N 条日志 sync 一次 + 最后强制 sync - 在日志开头写固定长度头(如 4 字节 int 表示正文长度),写入时先
seek()到末尾,writeInt(len),再write(byte[]);崩溃后可通过头长度校验识别残缺条目 - 不要依赖
seek()定位“上一条结尾”来修复——文件锁只保原子性,不保一致性;修复逻辑应由独立的 log-rotator 进程承担
真正难的不是调通 seek() 和 tryLock(),而是想清楚:你愿为 0.1% 的崩溃丢失率付出多少吞吐下降,以及是否准备好处理锁冲突时的日志降级策略。










