RandomAccessFile随机IO在java中是一个重要的IO类,与传统的IO类相比有很多特点:
1.集成了IO读写方法,运用这个类就可以对文件内容进行读写操作。
2. getFilePointer()方法,可以获取当前文件读取/写入的位子,类似于获取文件中当前光标位置。
3. seek(pos),指定文件的光标位置,通俗点说就是指定你的光标位置然后下次读文件数据的时候从该位置读取。
有了这个特性,可以实现一些操作,例如文件断点续传:文件下载过程中网断了,记录文件下载位置下次网连接上了直接从该位置开始下载;
多线程下载文件:或将一个大的文件分成多个部分,然后用多线程每个线程负责读取/写入其中一段。
4. FileChannel 它返回的就是nio通信中的file的唯一channel
RandomAccessFile raf= new RandomAccessFile("filePath","rw");FileChannel fileChannel = raf.getChannel();
java NIO核心组成是:
1). channel:管道,与stream相比它是双向的,是面向缓冲区ByteBuffer的,对应于stream的输入流,是ByteBuffer从channel读入数据,对应于stream的输出流,是ByteBuffer从channel写出数据 。
inputstream ---->fileChannel.read(buf)
outputstream ----->fileChannel.write(buf)
2). ByteBuffer缓存区,大致分为三类:
ByteBuffer/DirectByteBuffer/MappedByteBufferByteBuffer:ByteBuffer.allocate(1024); 是存储在JVM堆空间的缓存区,优点是java用户态创建, 创建速度快,有jvm垃圾回收机制控制缓存区的回收。 缺点是占用jvm堆空间内存,增加jvm垃圾回收负担, 在文件上传或下载时需要调用操作系统read()/write() 函数,将缓存区数据copy到用户态内存空间(如read())/ 内核态内存空间(如write()函数),IO操作效率不高, 尤其是大文件。 DirectByteBuffer:ByteBuffer.allocateDirect(1024); 是在堆外申请了内存,缓存区存储在内核态内存中而不在 堆空间,通过虚拟内存地址,直接操作内核态内存空间 缓存区数据,IO操作不需要调用read()/write()系统 函数(0copy操作),并且不占用堆空间内存。 java是通过调用unsafe类实现的。MappedByteBuffer:虚拟内存映射,通过虚拟内存地址操作系统内存空间 缓存区数据,利用操作系统的内存的缺页中断机制 来加载文件系统的数据到内存中,MappedByteBuffer 是个抽象类,其实现类是DirectByteBuffer。实际 操作的类也是DirectByteBuffer类。
在进行文件上传下载时应用 DirectByteBuffer/MappedByteBuffer,不仅
逼格很高,而且速度也会得到提高,尤其是对于大型文件。
3) Selector待续。。。
FileChannel代码示例:
1)DirectByteBuffer上传文件
RandomAccessFile raf = null;
try {
raf = new RandomAccessFile("test.txt", "rw"); //获取fileChannel
FileChannel fileChannel = raf.getChannel();
// 申请直接内存,大小1024字节
ByteBuffer buf = ByteBuffer.allocateDirect(1024);
// ByteBuffer读入数据
int bytesRead = fileChannel.read(buf);
while (bytesRead != -1) {
// ByteBuffer由读状态转为写状态
buf.flip();
while (buf.hasRemaining()) {
// buf获取数据
System.out.print((char) buf.get());
}
// //压缩此缓冲区,将buffer中未读取的数据移到buffer前面
// //将光标指向最后一个没有读取的下一个位置
buf.compact();
// buf读数据,向buf写数据
bytesRead = fileChannel.read(buf);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (aFile != null) {
aFile.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
2)MappedByteBuffer 下载文件
RandomAccessFile raf = new RandomAccessFile("test.txt", "rw");
FileChannel fileChannel = raf.getChannel();
String msg = "你好,world!";
// 内存映射区域总大小
long size = msg.getBytes().length * COUNT;
//READ_WRITE可以读写的ByteBuffer
MappedByteBuffer map = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0, size);
for (int i = 0; i < COUNT; i++) {
map.put(msg.getBytes());
raf.close();
}
3)补充:
a)使用DirectByteBuffer/MappedByteBuffer,文件是存储在操作系统缓存页cachePage,写入磁盘是由操作系统控制的,如果cachePage还没有刷新到磁盘,如果系统正常关机,那么cachePage会刷到磁盘后再关机没有问题,但是如果是操作系统断电了,那就悲剧了!因为一些cachePage没写入磁盘,会导致一些文件丢失。
解决:1.通过命令将pageCache刷新到磁盘,但是会影响系统性能
2.啥都不用管,依赖操作系统,效率佳,可能是断电这事比较少见吧。
b)虚拟内存是啥?
虚拟内存是操作系统的内存管理机制,linux操作系统为每个系统进程如java进程,分配一个虚拟内存,虚拟内存是逻辑内存,理论大小是2^32(32位操作系统),2^64(64位操作系统)。虚拟内存与实际物理内存是通过虚拟内存地址(逻辑地址)+MMU(内存管理硬件)得出实际物理内存地址。