FileChannel
FileChannel 可以通过 RandomAccessFile 获取,或者FileChannel.open,亦或 IS/OS 获取。write 和 read 都是通过 ByteBuffer 来存储。
FileChannel.open 时可以提供 OpenOption 来定义行为,如果需要写的话可以使用 write 和 append 模式,在不确定文件是否存在是加入 Create,这样如果不存在会自动创建。
write 和 append 有什么区别?
这两种模式声明的不是 FileChannel 的模式,而是声明那个文件的打开模式,作为 FileChannel 只顾自己position 增加,在 write 模式下文件的 postion 跟 Channel 的 position 是一致的,但是在 append 模式下文件 position 跟 Channel 的完全脱节,两者都是自顾自增加。
说到这里你可能已经想到,如果要实现每次输入内容覆盖之前的话,必须选择 Write 模式,并且每次 channel.write 前都需要将channel的 position 置为零。
文件锁 Lock
FileChannel.lock 和 tryLock从文档上看一个是同步阻塞、另一个是非阻塞。
tryLock 在同一个JVM中不同线程获取时,先到先得,后到的返回null,但我在windows上测试为抛出异常:OverlappingFileLockException,据说 Linux 上抛出【java.io.IOException:Permission denied】。
tryLock 和 lock 都提供一个API含有三个参数
lock(long position,
long size,
boolean shared)
看样子貌似可以锁住部分似的,可能跟操作系统有关,反正windows上并不行,抛出异常:
java.io.IOException: 另一个程序已锁定文件的一部分,进程无法访问。
共享锁,独占锁概念上跟并发的 WriteReadLock一样可多读但只一写,写是独占的。
怎么设置?看上面API第三个参数。
lock(long position,
long size,
boolean shared) // 是否共享锁
如何判断获取到的是什么锁?
FileLock.isShared()
实现靠谱的文件锁
主要就是一个循环判断加try.catch
while (true) {
try {
lock = fc.lock();
break;
} catch (OverlappingFileLockException e) {
Thread.sleep(1 * 1000);
}
}
这可以让需要获取锁的代码块阻塞在那里,当然咯,如果想 wait 也是可以的,只是要在 release 的地方加上 notifyAll 了。
内存映射文件
这个可谓 “大杀器”,特别是大文件读写时,效率比普通IO要快N倍。据一位网友测试86M的文件进行读操作,内存映射方式只要78ms,而普通IO需要468ms,差了6倍。可见威力无穷。
为什么这么快?
普通IO是操作系统先读入到内核缓冲器,再转到用户进程私有的内存区,当然JVM进程还作了内核态和用户态的切换;而内存映射方式,是将文件直接映射到内存中的一块区域,当调用读时会抛出缺页异常,OS会读取该页数据,依次往复,因此这里少了依次内核缓冲区到私有内存之间的拷贝,所以快了不少。
内存映射模式
read_only,只读设置
read_write,读写都可,并且任何写操作立即反应在文件上,其他共享该文件的内存映射也能立即看到
private,私有模式,不写时跟read_only 一样,但是写时会克隆一个独立内存区域,不会影响文件。
代码片段
File file = new File("E:\\test.txt");
FileInputStream in = new FileInputStream(file);
FileChannel channel = in.getChannel();
MappedByteBuffer mappedBuffer = channel.map(FileChannel.MapMode.READ_ONLY, 0,channel.size());