[size=medium] 最近项目里碰到了个问题,在Linux环境上,Thread1通过java.nio.channels.FileLock给文件加锁,通过Thread2竟然能删除这个文件,突然感觉很奇怪,Windows环境上不是这样的行为啊,因此就顺便研究了下java文件锁的机制。
首先看一下如何简单实现一个java的文件锁[/size]
[size=medium] FileLock是通过调用FileChannel的lock或tryLock方法来获取的,lock获取不到当前线程会处于阻塞等待状态,tryLock不管是否成功获得锁都会立即返回。
FileLock分两种,共享锁(shared)和排它锁(exclusive),多个线程可以同时持有同一个文件的共享锁,但但如果有一个线程持有了一个文件的排它锁,则其它线程将无法获取该文件的锁(包括共享锁和排它锁),这里有一点要特别留意,并不是所有的操作系统都支持共享锁,如果某个操作系统不支持共享锁,那么FileChannel的lock或tryLock会自动返回排它锁,如何判断一个FileLock为共享锁还是排它锁,只需要调用lock.isShared方法即可,返回true则为共享锁,false为排它锁。
那接下来回到我们之前的问题,为什么Linux环境下,Thread1持有File1的lock,但是Thread2却能删除File1,我们知道,对于结构型数据库,如果一个存储过程对某一张表中的某一条记录加锁,那另一个存储过程是无法删除该条记录的,这是因为删除一条记录需要获取该记录的独占锁,但是一条记录的独占锁同一时刻只能由一个存储过程获取,所以很显然,这里是删除失败(关于数据库的锁目前就点到为止,不是我们这篇文章需要讨论的范围),那么这里很容易让我们想到,难道Thread2在删除File1的时候不需要获取该文件的锁?这样不是非常不安全吗?带着这个疑问,我查了java api发现,原来锁在操作系统级别有两个特征,协同(advisory)和强制(mandatory),这个是平台相关的,windows平台的锁是mandatory的,linux平台的锁则是advisory的,关于这两种特征的行为,大致可以总结为以下两点,advisory特征的锁并不会阻止其它线程对该文件的访问甚至删除,但mandatory特征的锁会完全阻止其它线程对文件的任何违反锁规则的访问(比如read并不会违反锁规则,但update或者delete却是违反了锁规则),而且java api也明确的告诉我们不要希望lock来帮你阻止其它程序对文件的访问。
该贴持续更新,会加入更多实验数据
[/size]
首先看一下如何简单实现一个java的文件锁[/size]
package com.pracbiz.b2bportal.core.eai.backend;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
public class TestLock
{
public static void main(String[] args) throws IOException, InterruptedException
{
RandomAccessFile input = null;
FileChannel channel = null;
FileLock lock = null;
final File source = new File("/Users/youwenwu/Downloads/a.txt");
try
{
if (source.exists())
{
input = new RandomAccessFile(source, "rw");
//step 1.获取FileChannel,获取channel有3种方式,分别是调用
//FileInputStream,FileOutputStream以及RandomAccessFile
//实例的getChannel方法.
channel = input.getChannel();
//step 2.获取FileLock
lock = channel.tryLock();
}
}
finally
{
if (lock != null)
{
lock.release();
lock = null;
}
if (channel != null)
{
channel.close();
channel = null;
}
if (input != null)
{
input.close();
input = null;
}
}
}
}
[size=medium] FileLock是通过调用FileChannel的lock或tryLock方法来获取的,lock获取不到当前线程会处于阻塞等待状态,tryLock不管是否成功获得锁都会立即返回。
FileLock分两种,共享锁(shared)和排它锁(exclusive),多个线程可以同时持有同一个文件的共享锁,但但如果有一个线程持有了一个文件的排它锁,则其它线程将无法获取该文件的锁(包括共享锁和排它锁),这里有一点要特别留意,并不是所有的操作系统都支持共享锁,如果某个操作系统不支持共享锁,那么FileChannel的lock或tryLock会自动返回排它锁,如何判断一个FileLock为共享锁还是排它锁,只需要调用lock.isShared方法即可,返回true则为共享锁,false为排它锁。
那接下来回到我们之前的问题,为什么Linux环境下,Thread1持有File1的lock,但是Thread2却能删除File1,我们知道,对于结构型数据库,如果一个存储过程对某一张表中的某一条记录加锁,那另一个存储过程是无法删除该条记录的,这是因为删除一条记录需要获取该记录的独占锁,但是一条记录的独占锁同一时刻只能由一个存储过程获取,所以很显然,这里是删除失败(关于数据库的锁目前就点到为止,不是我们这篇文章需要讨论的范围),那么这里很容易让我们想到,难道Thread2在删除File1的时候不需要获取该文件的锁?这样不是非常不安全吗?带着这个疑问,我查了java api发现,原来锁在操作系统级别有两个特征,协同(advisory)和强制(mandatory),这个是平台相关的,windows平台的锁是mandatory的,linux平台的锁则是advisory的,关于这两种特征的行为,大致可以总结为以下两点,advisory特征的锁并不会阻止其它线程对该文件的访问甚至删除,但mandatory特征的锁会完全阻止其它线程对文件的任何违反锁规则的访问(比如read并不会违反锁规则,但update或者delete却是违反了锁规则),而且java api也明确的告诉我们不要希望lock来帮你阻止其它程序对文件的访问。
该贴持续更新,会加入更多实验数据
[/size]