FileChannel.lock()加的是操作系统级建议性排他锁,仅对同样使用NIO加锁的进程生效,作用于指定字节范围,需手动release或随channel关闭自动释放。

FileChannel 的 lock() 方法可以对文件的指定区域加排他锁(写锁),防止其他进程或线程同时修改该区域。关键在于:它加的是操作系统级的 advisory lock(建议性锁),不是强制锁,依赖所有访问方主动遵守。
理解 lock() 的行为和限制
FileChannel.lock() 返回一个 FileLock 对象,代表当前 JVM 在该文件某段区域上持有的锁。注意以下几点:
- 锁是“建议性”的:只有同样使用 NIO FileChannel(或兼容系统调用)尝试加锁的进程才会被阻塞;直接用 FileOutputStream 写文件不会受此锁影响
- 锁作用于文件**字节范围**,不是整个文件;可对同一文件不同区域加多个互不重叠的锁
- 默认为排他锁(exclusive),不能与其它排他锁或共享锁共存;若需共享锁,需显式调用
lock(position, size, true) - 锁与 FileChannel 绑定,channel 关闭或 JVM 退出时自动释放;也可手动调用
fileLock.release()
基本加锁操作示例
以下代码对文件前 1024 字节加排他锁,并确保及时释放:
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
RandomAccessFile raf = new RandomAccessFile("data.txt", "rw");
FileChannel channel = raf.getChannel();
try {
// 尝试获取 [0, 1024) 区域的排他锁,阻塞直到成功
FileLock lock = channel.lock(0, 1024, false);
try {
// 安全地读写该区域
ByteBuffer buf = ByteBuffer.allocate(100);
channel.read(buf, 0);
buf.flip();
// ... 处理数据
} finally {
lock.release(); // 必须显式释放,避免死锁或资源占用
}
} catch (OverlappingFileLockException e) {
// 当前 JVM 中已有其他线程锁了重叠区域
} catch (IOException e) {
// 文件不可访问、磁盘满等
} finally {
channel.close();
raf.close();
}
非阻塞加锁与超时控制
如果不想阻塞,可用 tryLock():
立即学习“Java免费学习笔记(深入)”;
-
channel.tryLock():尝试锁定整个文件,立即返回 null 表示失败 -
channel.tryLock(pos, size, shared):尝试锁定指定区域,返回 null 表示已被占用 - 如需超时,需自行结合循环 + Thread.sleep() 实现,例如最多等待 3 秒:
FileLock lock = null;
long deadline = System.currentTimeMillis() + 3000;
while (lock == null && System.currentTimeMillis() < deadline) {
lock = channel.tryLock(0, 1024, false);
if (lock == null) {
Thread.sleep(100); // 短暂让出 CPU
}
}
if (lock == null) {
throw new RuntimeException("无法在限定时间内获取文件锁");
}
跨进程协作注意事项
排他锁真正起作用的前提是:所有访问该文件的程序都使用相同机制(如 FileChannel.lock 或 POSIX flock/fcntl)。常见误区:
- Java 进程 A 加了锁,Python 进程 B 若用 open()+write() 直接写,会绕过锁——必须在 Python 中也调用
flock()或fcntl(...F_SETLK...) - 同一 JVM 内多个 FileChannel 操作同一文件句柄,可能触发 OverlappingFileLockException;应确保通道来源统一(如共用同一个 RandomAccessFile)
- Windows 和 Unix 行为略有差异:Windows 锁定期间文件无法被删除或截断;Linux 下锁随 close() 自动释放,但 fork 出的子进程会继承锁(通常不推荐)

















