Java中文件读写,我们如果是从J2SE学起的都晓得InputStream,OutputStream流式读写,以及随机读写RandomAccessFile。但是从Java1.4后新增了Java NIO包,里面包含了
一个平时我们不太用的类ByteBuffer字节缓冲区来进行文件读写,说到字节缓冲区它又分为两种方式,一种是非直接缓冲区读写,另一种是直接缓冲区读写,还有一种MappedByteBuffer内存映射方式进行读写。
现在我们来说说这几种的区别:
1.InputStream流式文件读写,实际上是调用操作系统的read()和write()来完成文件的读写,并非直接对文件操作。
2.随机读写实际上是继承了DataInputStream流,它可以对java基础类型进行操作、而且是即可读取、也可以写入,这里的随机并不是不可控制、不可预测的去访问文件、而是可以通过指针的形式定位到具体的位置——“文件指针”、具体使用“文件指针”的方法:调用此流的seek(long n)方法来设置位置。
3.非直接缓冲区:使用缓冲区来进行文件读写实际上是减少了系统调用次数,可以预读更多的字节到自己维护的字节缓冲区中。
4.直接缓冲区:又称为堆外内存,属于Native堆,不属于JVM堆,是由C语言中的malloc()分配的内存。所以在某些场景中可以提高性能。当然每一次Full GC时,它的内存也会被JVM回收。
5.内存映射:它也属于堆外内存并且是直接缓冲区的一种,但是它相比于DirectMemory来说,它是由Java进程直接建立起来的某一段虚拟地址空间和文件对象的关联映射关系。所以内存映射文件的区域并不在JVM GC的回收范围内,因为它本身就不属于堆区,Java中使用((DirectBuffer)byteBuffer).cleaner().clean()来回收。
以下写出后四种的例子:
6.比较下DirectBuffer和MappedByteBuffer:
前者的文件复制原理是:
FileChannel inChannel = new FileInputStream(srcFile).getChannel();
FileChannel outChannel = new FileOutputStream(dstFile).getChannel();
//接下来就是开辟一段操作系统内存ByteBuffer,然后inChannel.read(byteBuffer);
outChannel.write(byteBuffer);//写入通道最终写入磁盘
然而内存映射就省去了操作系统内存缓冲这样的操作,而是将目地物理存储直接作为虚拟内存
那么也就是将outChannel.map(MapMode.READ_WRITE,position,copyFileSize);这段意思就是将输出通道直接作为一段虚拟内存了,那么也就是只需要让
inChannel.read(mappedByteBuffer);也就是只需要让源文件读到虚拟内存中,实际上也就是直接的写入到了目地物理存储了。
7.什么是Buffer:
首先,buffer就是我们常说的块,而它本质上就是一个字节数组,相比于流式一个字节一个字节的读写,块读写减少了读写次数。
package com.roy.buffer;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
/***
*
* @author SAMSUNG
* 本代码讨论Java中文件读写的四种方式:RandomAccessFile,InputStream,
* 内存映射,堆外内存(DirectMemory),非直接缓冲区读写
*/
public class FileReadTs {
public static void main(String[] args) {
File f = new File("D:\\ExtJS\\Ext JS高级程序设计.pdf");//83M
FileReadTs frt = new FileReadTs();
frt.random(f);
frt.mapped(f);
frt.buffer(f);
frt.directBuffer(f);
}
//随机读写
public void random(File f){
try {
RandomAccessFile raf = new RandomAccessFile(f,"rw");
int len = (int)f.length();
byte [] b = new byte[len];
long start = System.currentTimeMillis();
while(raf.read(b)!=-1){
}
long end = System.currentTimeMillis();
System.out.println("随机读写 Waste Time:"+(end-start));
raf.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
//内存映射
public void mapped(File f){
try {
FileInputStream fis = new FileInputStream(f);
FileChannel channel = fis.getChannel();
MappedByteBuffer mbb = channel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size());
byte [] b = new byte[1024];
int len = (int) f.length();
long start = System.currentTimeMillis();
for(int offset = 0 ; offset<len;offset+=1024){
if(len-offset>1024){
mbb.get(b);
}
else {
mbb.get(new byte[len-offset]);
}
}
long end = System.currentTimeMillis();
System.out.println("内存映射 Waste Time:"+(end-start));
fis.close();
channel.close();
mbb.clear();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
//非直接缓冲区IO
public void buffer(File f){
try {
FileInputStream fis = new FileInputStream(f);
FileChannel channel = fis.getChannel();
ByteBuffer bb = ByteBuffer.allocate(1024);
long start = System.currentTimeMillis();
while(channel.read(bb)!=-1){
bb.flip();
bb.clear();
}
long end = System.currentTimeMillis();
System.out.println("非直接缓冲区 Waste Time:"+(end-start));
fis.close();
channel.close();
bb.clear();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
//直接缓冲区读写
public void directBuffer(File f){
try {
FileInputStream fis = new FileInputStream(f);
FileChannel channel = fis.getChannel();
ByteBuffer bb = ByteBuffer.allocateDirect(1024);
long start = System.currentTimeMillis();
while(channel.read(bb)!=-1){
bb.flip();
bb.clear();
}
long end = System.currentTimeMillis();
System.out.println("直接缓冲区 Waste Time:"+(end-start));
fis.close();
channel.close();
bb.clear();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
运行结果(单位是毫秒):
随机读写 Waste Time:115
内存映射 Waste Time:45
非直接缓冲区 Waste Time:215
直接缓冲区 Waste Time:165
下面我需要再介绍一种多线程随机读写,当然对于熟悉多线程的Coder来说太简单不过了。
package com.roy.buffer;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
/***
* 多线程对文件进行读写
* @author SAMSUNG
*
*/
public class ThreadRandomFile {
public static void main(String[] args) {
String m1="这是我要写入的第一个";
String m2="这是我要写入的第二个";
String m3="这是我要写入的第三个";
String m4="这是我要写入的第四个";
//这里请注意,多线程有两种实现方式,extends Thread和implements Runnable接口
//一般的我们需要使用的是实现接口,因为这样才能达到资源贡献的目的
//否则,如果使用继承Thread则会导致线程之间隔离
new Thread(new FileRandomThread(0,m1.getBytes())).start();
new Thread(new FileRandomThread(1024*1,m2.getBytes())).start();
new Thread(new FileRandomThread(1024*2,m3.getBytes())).start();
new Thread(new FileRandomThread(1024*3,m4.getBytes())).start();
}
}
class FileRandomThread implements Runnable{
private int skip;
private byte[] content;
public FileRandomThread (int skip,byte[] content){
this.skip=skip;
this.content=content;
}
@Override
public void run() {
try {
RandomAccessFile raf = new RandomAccessFile(new File("c:\\m.txt"),"rw" );
raf.seek(skip);
raf.write(content);
raf.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
这里再解释一遍,我们使用的实现Runnable方式来实现多线程,而不是继承Thread,其原因就是使用前者可以使线程间共享内存变量。
具体到我们可以来查看https://github.com/eagle2088/thinkInJavaSty/tree/master/src/com/roy/thread源代码来学习下两者的区别,我这里也是很久没写代码了,所以以此来回顾回顾,最近已经开始了筹备2015年找工作的计划了,不能再在三星待下去了,大好的青春浪费在这里太不值当。