在Linux下正确的使用Java锁

最近同事在linux上部署一个开源项目,为了防止这个项目生成的文件被外部其他程序修改/删除掉,他们稍微修改了下源代码在文件生成后给文件加锁,在他们开发机(Windows)上测试后没问题,就部署到服务器(CentOS)上了,结果不行,锁不住,生成的文件可以被第三方程序修改删除掉。
于是叫我帮忙调一下,因为我开发机用Linux Mint。

看现象十之八九就是 建议锁(ADVISORY)的问题,所以也懒的看。

猜测他们源码大体是这么写的:

1
2
3
4
5
6
7
8
9
10
FileChannel channel = new RandomAccessFile(file, "rw").getChannel();
try(FileLock lock = channel.tryLock()) {
if (lock == null) {
log.debug("lock on file:{}", file.getPath());
// do something
// 花了很多时间
} else {

}
}

首先是结论:
代码没问题,不需改动所以不需要调什么。

Linux文件锁的类型有两种:

  • 1.mandatory 强制锁,加上后第三方程序对加过锁的文件修改会报错:文件被占用、文件无法被修改等。这个是排它的,一般有这种需求的都想要这种锁。
  • 2.advisory 建议锁/劝告锁,加上后第三方程序(gedite、vim/vi等、touch、rm等)可以直接修改删除。这个是 建议 的,也就是说它确实提供了加锁和检测是否有锁的手段,但假设你的第三方程序根本不检测文件有没有锁就直接修改/删除了,它也不 排斥,所以只对那些修改前try一下的守规矩程序有效。

再看一下JDK里FileLock的注释:

1
2
3
4
5
6
7
8
9
10
11
12
Whether or not a lock actually prevents another program from accessing
the content of the locked region is system-dependent and therefore
unspecified. The native file-locking facilities of some systems are merely
advisory, meaning that programs must cooperatively observe a known
locking protocol in order to guarantee data integrity. On other systems
native file locks are mandatory, meaning that if one program locks a
region of a file then other programs are actually prevented from accessing
that region in a way that would violate the lock. On yet other systems,
whether native file locks are advisory or mandatory is configurable on a
per-file basis. To ensure consistent and correct behavior across platforms,
it is strongly recommended that the locks provided by this API be used as if
they were advisory locks.

大意为是否能阻止另外的程序访问依赖于操作系统,有些操作系统下是advisory,有些则是mandatory。

一般来说绝大部分发行版(Ubuntu、CentOS、Debian)等默认获取的都是advisory锁,可以使用命令:

1
cat /proc/locks

看下当前系统所有已有的锁的类型。

如果想在Linux下mandatory锁,需要在操作系统挂载文件系统时激活才可以。

1
mount -o mand /dev/sdb7 /mnt