Java中FileLock的实现细节

起因

开始看Lucene源代码,找了个最简单的FSLockFactory开始看。
然而还是看出了不明白的地方

在NativeFSLockFactory的close方法中有这么一段注释

1
// NOTE: we don't validate, as unlike SimpleFSLockFactory, we can't break others locks

我从网上找到了当初的Bug的讨论帖
https://issues.apache.org/jira/browse/LUCENE-6507

从里面讨论了NativeFSLock了一些关于这个文件锁的实现的问题

JDK的BUG

On some systems, closing a channel releases all locks held by the Java virtual machine on the underlying file regardless of whether the locks were acquired via that channel or via another channel open on the same file. It is strongly recommended that, within a program, a unique channel be used to acquire all locks on any given file.

这个JDK的文档中写的就是这么一种场景,在某些操作系统上,如果我们把channel直接关闭,那么其他的在这个文件上的Lock会直接失效,当然前提是在同一个JVM进程中

如果没有这个BUG,我们会怎么写这个LOCK的获取代码呢

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
FileChannel channel = null;
FileLock lock = null;
try {
channel = FileChannel.open(realPath, StandardOpenOption.CREATE, StandardOpenOption.WRITE);
lock = channel.tryLock();
if (lock != null) {
return new NativeFSLock(lock, channel, realPath, creationTime);
} else {
throw new LockObtainFailedException("Lock held by another program: " + realPath);
}
} finally {
if (lock == null) { // not successful - clear up and move out
IOUtils.closeWhileHandlingException(channel); // TODO: addSuppressed
clearLockHeld(realPath); // clear LOCK_HELD last
}
}

很简单的,就是先拿channel,然后tryLock,如果失败,就是记得关闭channel就行了。

但是有个JDK的这个问题,还能直接关闭吗?
显然是不能的,你关闭channel,会把其他在这个channel的锁给invalid掉。

Lucene的实现

这个帖子最后的解决方案,就是加了一个类似于双重校验锁的东西。
在同一个进程中,设置一个
Set<String> LOCK_HELD

要想获取文件锁,首先得成功把LockName加个Lock_HELD中。

参考

java.nio.channel.FileLock注释