在代码中,可以通过 synchronized 关键字对代码片段进行加锁,假设我们需要对文件进行加锁,synchronized 只能对 JAVA 执行代码进行加锁,倘若另外一个操作文件的线程是操作系统中其他的某个本地线程呢?这时仅仅通过 synchronized 关键字来加锁显然是不行的。
但好在 JDK 1.4 引入了针对本地操作系统的文件加锁机制,下面便和大家一起来学习。
public static void main(String[] args) throws IOException, InterruptedException {
//获取文件,第二个参数为是否追加写,不设置为默认覆盖整个文件
FileOutputStream fos = new FileOutputStream("D:\\test\\bigFile.txt", true);
//获取文件通道
FileChannel fc = fos.getChannel();
//尝试对文件加锁
FileLock fl = fc.tryLock();
// FileLock不等于null则加锁成功
if (fl != null) {
System.out.println("Locked File");
//指定线程睡眠10秒
TimeUnit.SECONDS.sleep(10);
//释放锁
fl.release();
System.out.println("Released Lock");
}
//关闭流
fos.close();
}
Locked File
Released Lock
getChannel() 获取通道 FileChannel ,它可以调用 tryLock() 和 lock() 对文件进行加锁。
tryLock() 是非阻塞式的,它设法获取锁,如果获取不到(其他线程已拿到锁,并且不共享锁时),则会直接返回。
lock() 是阻塞式的,它会一直阻塞直到可以获得锁,或者调用 lock() 的线程中断、调用 lock() 的通道关闭。
release() 会释放锁。
我们也可以对文件的一部分加锁:
tryLock(long position, long size, boolean shared)
lock(long position, long size, boolean shared)
第一个参数为起始位置,第二个参数为结束位置,第三个参数为是否为共享锁。
值得一提的是,无参数的加锁方法会对整个文件进行加锁,加锁区域会随着文件的尺寸而进行变化;固定尺寸的锁不随文件尺寸做出变化,即超出 size - position 之外的区域是不会被锁定的。
操作系统必须支持共享锁才可以进行使用,可以通过 isShared() 来查询。
最后,演示一个通过映射文件的部分进行加锁的例子,不同的线程对文件的不同部分进行加锁,两者可以同时修改,像我们的数据库就是基于这种原理。
public class LockingMappedFiles {
//128MB
private static final int LENGTH = 0X8FFFFFF;
private static FileChannel fc;
private static class LockAndModify extends Thread {
private ByteBuffer buffer;
private int start, end;
public LockAndModify(ByteBuffer mbb, int start, int end) {
this.start = start;
this.end = end;
mbb.limit(end);
mbb.position(start);
//截取position至limit之间的区域作为新缓冲区
buffer = mbb.slice();
start();
}
@Override
public void run() {
try {
FileLock fl = fc.lock(start, end, false);
System.out.println("Locked:" + start + " to " + end);
while (buffer.position() < buffer.limit() - 1) {
//将x的ASCII码+1变成y
buffer.put((byte) (buffer.get() + 1));
}
fl.release();
System.out.println("Released:" + start + " to " + end);
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws IOException {
fc = new RandomAccessFile("D:\\test\\bigFile.txt", "rw").getChannel();
MappedByteBuffer out = fc.map(FileChannel.MapMode.READ_WRITE, 0, LENGTH);
for (int i = 0; i < LENGTH; i++) {
out.put((byte) 'x');
}
new LockAndModify(out, 0, 0 + LENGTH / 3);
new LockAndModify(out, LENGTH / 2, LENGTH / 2 + LENGTH / 4);
}
}
```java
Locked:0 to 50331647
Locked:75497471 to 113246206
Released:75497471 to 113246206
Released:0 to 50331647
本次分享至此结束,希望本文对你有所帮助,若能点亮下方的点赞按钮,在下感激不尽,谢谢您的【精神支持】。
若有任何疑问,也欢迎与我交流,若存在不足之处,也欢迎各位指正!