今天在分析HDFS数据节点的源码时,了解到在数据节点的文件结构中,当数据节点运行时,${dfs.data.dir}下会有一个名为”in_use.lock”的文件,该文件就是文件锁。
文件加锁是 JDK1.4 引入的一种机制,它允许我们同步访问某个作为共享资源的文件。竞争同一文件的两个线程可能在不同的 Java 虚拟机上,或者一个是 Java 线程,另一个是操作系统中的某个本地线程。文件锁对其他的操作系统进程是可见的,因为 Java 的文件加锁直接映射到了本地操作系统的加锁工具(通过文件进行加锁)。
在javaNIO中提供了文件锁的功能,这样当一个线程获取文件锁后,才可以操作文件,其他线程是无法操作文件的,要想进行文件锁定的操作,则要使用FileLock类完成,此类的对象需要依靠FileChannel进行实例化。
java文件锁要么独占,要么共享。
共享锁:允许多个线程对文件进行读操作。
独占锁:只允许一个线程进行文件的读写操作
Trylock 与 lock 方法
tryLock(position,size,isShare); 第三个参数为 true 时为共享锁
tryLock() 是非阻塞式的,它设法获取锁,但如果不能获得,例如因为其他一些进程已经持有相同的锁,而且不共享时,抛出文件重叠锁异常【OverlappingFileLockException】。
lock() 是阻塞式的,它要阻塞进程直到锁可以获得,或调用 lock() 的线程中断,或调用 lock() 的通道关闭。
OverlappingFileLockException
单个 Java 虚拟机在某个特定文件上所保持的锁定、不同 jvm 或者不同操作系统获取同一文件锁时,先拿到的获得锁,后获取的抛出文件重叠锁异常【OverlappingFileLockException】。以上是 windows 才会出现如此现象,如果是linux会抛出异常:【java.io.IOException: Permission denied 】
测试代码如下:
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.channels.OverlappingFileLockException;
public class StorageDirectory {
public static final String STORAGE_FILE_LOCK = "in_use.lock" ;
File root ; //root directory
java.nio.channels.FileLock lock ; //storage lock
/** * Lock storage to provide exclusive access. * * <p> if locking is supported we guarantee exculsive access to the * storage directory . Otherwise, no guarantee is given * * @throws Exception if locking file */
public StorageDirectory(File dir){
this.root = dir ;
}
public void lock() throws Exception{
this.lock = tryLock() ;
if(lock == null){
String msg = "Cannot lock storage" + this.root + ". The directory is already locked." ;
System.err.println(msg) ;
throw new IOException(msg) ;
}
}
public void unlock() throws IOException{
if(this.lock == null)
return ;
this.lock.release() ;
lock.channel().close() ;
lock = null ;
}
/** * Attempts to acquire an exclusive lock on the storage * @return A lock object representing the newly-acquired lock or null if * storage is already lockd . * @throws IOException */
java.nio.channels.FileLock tryLock() throws IOException{
boolean deletionHookAdded = false ;
File lockF = new File(root,STORAGE_FILE_LOCK) ;
//如果没有文件
if(!lockF.exists()){
lockF.deleteOnExit() ;
deletionHookAdded = true ;
}
RandomAccessFile file = new RandomAccessFile(lockF,"rws") ;
java.nio.channels.FileLock res = null ;
try{
res = file.getChannel().tryLock() ;
}catch(OverlappingFileLockException oe){
file.close() ;
return null ;
}catch(IOException e){
System.err.println("Cannot create lock on " + lockF) ;
file.close() ;
throw e ;
}
if(res !=null && !deletionHookAdded){
//if the file existed prior to our startup, we didn't
//call deleteOnExit above. But since we successfully locked
//the dir , we can take care of cleaning it up
lockF.deleteOnExit() ;
}
return res ;
}
}
测试类
import java.io.File;
import java.io.IOException;
class Operation implements Runnable{
private StorageDirectory sd ;
public Operation(StorageDirectory sd){
this.sd = sd ;
}
public void run() {
try {
System.out.println("文件加锁");
sd.lock() ;
} catch (Exception e) {
System.out.println("文件加锁失败") ;
return ;
}
try {
Thread.currentThread().sleep(1000) ;
} catch (InterruptedException e) {
e.printStackTrace();
}finally{
try {
System.out.println("释放文件锁");
sd.unlock() ;
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public class Main {
/** * @param args */
public static void main(String[] args) throws Exception{
File dir = new File("G:\\eclipse-SDK-4.2.2-win32-x86_64\\workspace\\FileLock") ;
StorageDirectory sd1 = new StorageDirectory(dir) ;
new Thread(new Operation(sd1)).start() ;
// Thread.sleep(2000) ;//注释掉文件加锁失败,解开注释文件加锁成功
StorageDirectory sd2 = new StorageDirectory(dir) ;
new Thread(new Operation(sd2)).start() ;
}
}