filelock 是基于操作系统的 advisory lock,需所有进程主动调用 lock() 才生效;必须通过 filechannel 获取,作用于文件或字节范围,jvm 退出时自动释放但建议显式 release();windows 下锁会阻塞文件打开,linux/macos 仅影响同样使用 flock/fcntl 的进程。

Java 中用 FileLock 实现文件级进程锁,核心是借助 FileChannel 的 lock() 方法获取操作系统级别的独占锁(或共享锁),从而在多个 JVM 进程间协调对同一文件的访问。它不是 Java 层面的线程锁,而是跨进程、由操作系统内核保证的锁,适合分布式场景下轻量级资源互斥。
锁的本质和前提条件
FileLock 是基于操作系统的 advisory lock(建议性锁),意味着:只有所有参与者都主动调用 lock() 并检查返回结果,锁才真正起作用;绕过该机制直接读写文件,锁不会自动阻止——这点必须明确。
使用前需注意:
- 必须通过
FileChannel获取,而FileChannel通常来自RandomAccessFile、FileInputStream.getChannel()或FileOutputStream.getChannel() - 锁作用于整个文件或指定字节范围,不能按逻辑“资源名”锁(比如想锁 “config.properties” 就得真的打开并锁定该文件)
- 锁与底层文件描述符绑定,JVM 退出或通道关闭时会自动释放(但异常未关闭可能延迟释放,建议显式
release()) - Windows 下锁会阻塞其他进程打开同一文件(即使是只读),Linux/macOS 行为更宽松(仅影响同样调用
flock/fcntl的进程)
基本用法:独占锁示例
以下是一个安全获取文件锁并执行操作的典型模式:
// 假设要保护 /tmp/myapp.lock 文件
File lockFile = new File("/tmp/myapp.lock");
RandomAccessFile raf = null;
FileChannel channel = null;
FileLock lock = null;
try {
raf = new RandomAccessFile(lockFile, "rw");
channel = raf.getChannel();
// 尝试获取独占锁(非阻塞)
lock = channel.tryLock();
if (lock == null) {
throw new RuntimeException("无法获取锁:其他进程正在运行");
}
// ✅ 此时已获得跨进程锁,可安全执行业务逻辑
System.out.println("获得锁,开始工作...");
} catch (IOException e) {
throw new RuntimeException("加锁失败", e);
} finally {
// 必须确保释放
if (lock != null && lock.isValid()) {
try { lock.release(); } catch (IOException ignored) {}
}
if (channel != null) {
try { channel.close(); } catch (IOException ignored) {}
}
if (raf != null) {
try { raf.close(); } catch (IOException ignored) {}
}
}
阻塞式加锁与超时控制
如果希望等待一段时间再放弃,可用 lock()(阻塞)配合线程中断,或自行封装带超时的逻辑:
-
channel.lock():阻塞直到成功或线程被中断 -
channel.tryLock(long start, long size, boolean shared):尝试锁定指定区间(如0, 1, false锁第一个字节实现轻量占位) - 实际项目中建议加超时:启动一个线程尝试加锁,主线程
join(timeout),超时则中断并清理
常见陷阱与建议
容易出错的地方:
-
不要用
FileOutputStream+getChannel()后直接lock():若流以append=true打开,某些系统可能不支持加锁 - 避免锁文件被误删:锁文件本身只是载体,删除它不会释放锁(锁由内核维护),但新进程会重新创建并尝试加锁——可能导致逻辑混乱。建议固定路径且不随意清理
-
慎用共享锁(
true):共享锁允许多个读进程共存,但 Java 没有强制约束写操作,仍需应用层协同设计 -
容器或云环境注意文件系统支持:NFS、某些 overlayfs 可能不完全支持
flock,测试务必覆盖部署环境
简单可靠的做法是:选一个无业务含义的锁文件(如 /var/run/myapp.pidlock),用 RandomAccessFile("rw") + tryLock() 做单实例校验,成功即运行主逻辑。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











