java nio的filelock是进程级锁,依赖操作系统实现,支持区域锁但跨平台行为不同,需显式释放且不适用于nfs或高并发场景。

Java NIO 的 FileChannel 提供的文件锁(FileLock)是**进程级**的,不是线程级的,因此可用于多进程间的并发控制。但要注意:它依赖操作系统原生支持,行为在不同平台(尤其是 Windows 和 Linux)上略有差异,且不能完全替代分布式锁或数据库事务。
文件锁是进程级互斥的
同一个 JVM 内多个线程调用 channel.lock() 不会阻塞彼此(除非显式使用 tryLock() 并检查返回值),因为 JVM 内部不维护跨线程的锁状态;而不同 JVM 进程(即不同 Java 程序或同一程序多次启动)对同一文件加锁时,操作系统会保证最多一个进程获得排他锁(exclusive)或共享锁(shared)。
关键点:
- 锁基于底层文件描述符和操作系统内核,JVM 无法绕过
- 必须通过
FileChannel获取,普通FileInputStream/FileOutputStream不支持 - 锁作用于文件的某一段(可指定起始位置和长度),不限于整个文件
正确获取文件锁的操作步骤
典型写法如下(以排他锁为例):
注意:必须使用 RandomAccessFile 或 FileOutputStream.getChannel() 等方式打开通道,并确保文件可写
- 用
RandomAccessFile raf = new RandomAccessFile("lock.txt", "rw")打开文件 - 调用
FileChannel channel = raf.getChannel() - 调用
FileLock lock = channel.lock()(阻塞直到获取)或channel.tryLock()(立即返回 null 表示失败) - 加锁成功后执行受保护的业务逻辑(如读写配置、生成唯一 ID、更新计数器等)
- 操作完成后必须显式
lock.release(),并channel.close()、raf.close()
⚠️ 锁在 Channel 关闭或 JVM 退出时自动释放,但**不能依赖自动释放**——异常时容易泄漏,务必放在 finally 或 try-with-resources 中管理。
常见陷阱与注意事项
实际使用中容易踩坑:
- Windows 下锁粒度更严格:即使只锁文件中间一段,整个文件也可能被锁定,其他进程无法读写任意部分;Linux 通常支持真正的区域锁
-
锁不阻止文件删除:在 Unix-like 系统中,已打开并加锁的文件即使被
rm删除,锁仍有效(因 inode 未释放),新进程创建同名文件不会冲突 - 不能跨 NFS 可靠工作:NFSv3 及更早版本不保证锁一致性,慎用于网络文件系统
-
没有锁超时机制:
lock()会无限等待,需自行结合tryLock()+ 循环 + sleep 实现带超时的获取逻辑
简单多进程协作示例(计数器场景)
假设两个 Java 进程要安全地对同一文本文件中的整数做原子自增:
- 进程 A 启动,打开
counter.txt,调用tryLock()成功 → 读出 100 → 写入 101 →release() - 进程 B 同时尝试
tryLock()返回 null → 等待 100ms 后重试 → 成功后读到 101 → 写入 102 - 只要所有进程都遵循“先锁、再读写、再释放”流程,就能避免覆盖
这不是高性能方案,但适合低频、简单协调场景(如防止重复启动、单实例校验、批处理任务分片)。高并发或分布式环境建议改用 Redis、ZooKeeper 或数据库行锁。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











