Java-NIO文件锁

作者:聂勇 欢迎转载,请保留作者信息并说明文章来源!

基本概念

文件锁类似于Java中的对象锁,可以锁定整个文件或文件的一部分,又分为独占锁、共享锁。
独占锁:也称排它锁,如果一个线程获得一个文件的独占锁,那么其它线程就不能再获得同一文件的独占锁或共享锁,直到独占锁被释放。
共享锁:如果一个线程获得一个文件的共享锁,那么其它线程可以获得同一文件的共享锁或同一文件部分内容的共享锁,但不能获取排它锁。

示例

FileLockThread.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
/**
* 建立时间:2008-10-28
*/
package cn.aofeng.nio;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.net.URL;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.nio.channels.OverlappingFileLockException;
import cn.aofeng.util.IOUtils;
/**
* 测试NIO中的文件锁:三个线程争抢文件锁,获得锁后向文件中写数据,然后再释放文件锁。
*
* @author aofeng <a href="mailto:aofengblog@163.com>aofengblog@163.com</a>
*/
public class FileLockThread implements Runnable {
public void run() {
Thread curr = Thread.currentThread();
System.out.println("Current executing thread is " + curr.getName());
URL url = FileLockThread.class.getResource("/cn/aofeng/nio/FileLockThread.txt");
RandomAccessFile raf = null;
FileChannel fc = null;
FileLock lock = null;
try {
raf = new RandomAccessFile(url.getPath(), "rw");
fc = raf.getChannel();
System.out.println(curr.getName() + " ready");
// 轮流获得文件独占锁。
while (true) {
try {
lock = fc.lock();
break;
} catch (OverlappingFileLockException e) {
Thread.sleep(1 * 1000);
}
}
if (null != lock) {
System.out.println(curr.getName() + " get filelock success");
fc.position(fc.size());
fc.write(ByteBuffer.wrap((curr.getName() + " write data. ").getBytes()));
} else {
System.out.println(curr.getName() + " get filelock fail");
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// 注意:要先释放锁,再关闭通道。
if (null != lock && lock.isValid()) {
try {
lock.release();
System.out.println(curr.getName() + " release filelock");
} catch (IOException e) {
e.printStackTrace();
}
}
IOUtils.close(fc);
IOUtils.close(raf);
}
}
/**
* @param args
*/
public static void main(String[] args) {
Thread t1 = new Thread(new FileLockThread());
t1.setName("t1");
Thread t2 = new Thread(new FileLockThread());
t2.setName("t2");
Thread t3 = new Thread(new FileLockThread());
t3.setName("t3");
t1.start();
t2.start();
t3.start();
}
}

代码执行结果

Current executing thread is t1
Current executing thread is t2
Current executing thread is t3
t1 ready
t3 ready
t2 ready
t3 get filelock success
t2 get filelock success
t3 release filelock
t2 release filelock
t1 get filelock success
t1 release filelock

从结果可以看出,各线程轮流获得文件锁,写入数据后然后释放锁。

提示:
1)由于各操作系统的锁实现会有不一致的地方,为了使代码方便移植,建议使用独占锁。
2)执行结果在不同的机器或每次执行结果都有可能与上述结果不一致,因为线程的执行顺序无法预料。
3)代码中的IOUtils.java 中的close()方法各位可自行实现或删除示例代码中的两行使用IOUtils.close()的代码,自行写代码关闭随机读写文件对象和文件通道。