文件锁
你有没有想过这个问题:
两个 JVM 同时写同一个文件,会发生什么?
答案是:文件内容会交错,甚至损坏。
两个进程同时写入同一个文件,没有任何协调机制,结果是不可预测的。
文件锁:多进程访问同一个文件
Java 通过 FileLock 实现文件锁,确保多进程访问同一文件时的数据一致性。
基本用法
获取锁
java
RandomAccessFile file = new RandomAccessFile("shared.txt", "rw");
FileChannel channel = file.getChannel();
// 获取排他锁(写锁)
FileLock lock = channel.lock(); // 阻塞直到获取锁
try {
// 临界区操作
file.writeBytes("data");
} finally {
lock.release(); // 释放锁
file.close();
}
// 非阻塞获取锁
FileLock tryLock = channel.tryLock();
if (tryLock != null) {
// 获取成功
try {
// 操作
} finally {
tryLock.release();
}
} else {
// 获取失败(已被其他进程持有)
}锁类型
java
// 排他锁(独占锁)
FileLock exclusiveLock = channel.lock();
// 共享锁(读锁)
FileLock sharedLock = channel.lock(0L, Long.MAX_VALUE, true); // true = 共享锁的范围
java
// 锁定文件的全部
FileLock fullLock = channel.lock();
// 锁定文件的指定范围
FileLock partialLock = channel.lock(100, 200, false); // 锁定 100~200 字节锁的特性
跨 JVM 有效
java
// JVM A
FileLock lock = channel.lock();
// JVM B 尝试获取锁,会阻塞(排他锁)或失败(共享锁)
// OS 层面的文件锁,对所有进程有效操作系统差异
java
// Windows:共享锁不真正共享,写锁独占
// Unix/Linux:支持真正的共享锁自动释放
java
// 关闭 Channel 时锁自动释放
channel.close(); // 锁也被释放
// 建议显式释放
lock.release();实战:多进程日志文件
java
public class ProcessSafeLogger {
private final FileChannel channel;
private final FileLock lock;
public ProcessSafeLogger(String path) throws IOException {
RandomAccessFile file = new RandomAccessFile(path, "rw");
channel = file.getChannel();
lock = channel.lock();
}
public void log(String message) throws IOException {
// 确保在锁内写入
byte[] data = (message + "\n").getBytes(StandardCharsets.UTF_8);
channel.write(ByteBuffer.wrap(data), channel.size());
}
public void close() throws IOException {
lock.release();
channel.close();
}
}NIO Files 的文件锁
java
// NIO Files 本身不直接支持文件锁
// 需要通过 FileChannel 获取锁
Path path = Path.of("shared.txt");
try (FileChannel ch = FileChannel.open(path,
StandardOpenOption.READ,
StandardOpenOption.WRITE)) {
FileLock lock = ch.lock();
try {
// 临界区操作
} finally {
lock.release();
}
}注意事项
锁的粒度
java
// ❌ 文件锁是粗粒度的
// 锁定整个文件,其他进程无法访问任何部分
FileLock lock = channel.lock();
// ✅ 细粒度:只锁定需要同步的部分
FileLock lock = channel.lock(position, size, shared);
// 其他进程可以访问锁范围之外的部分死锁
java
// ❌ 多个进程按不同顺序获取多个文件的锁 → 死锁
// JVM A: 锁 A → 锁 B
// JVM B: 锁 B → 锁 A
// 解决:所有进程按相同顺序获取锁总结
┌─────────────────────────────────────────────────────────────────┐
│ 文件锁:多进程访问同一文件的协调机制 │
│ │
│ 排他锁:阻止其他读写 │
│ 共享锁:允许并发读 │
│ 锁在关闭 Channel 或调用 release() 时释放 │
│ 文件锁是 OS 级别的,跨 JVM 有效 │
│ │
│ 口诀:多进程写同一文件,FileLock 来帮忙 │
└─────────────────────────────────────────────────────────────────┘