您的位置:首页 > 编程语言 > Java开发

Java NIO学习5(通道Channel)

2013-01-13 16:42 417 查看
在新的IO中,通道是一个可以用读取和写入数据的一种形式。

通道:Channel

1 通道可以用来读取和写入数据,通道类似于之前的输入、输出流,但是程序不会直接操作通道的,所有的内容都是先读到或写入到缓冲区之中,在通过缓冲区读取或者写入的

2 通道与传统的流操作不同,传统的流操作分为输入或输出流的,而通道本身是双向操作的,即可以完成输入也可以完成输出。



通道都是操作缓冲区完成的

String[] metas = { "hello world", "file channel" };
String destPath = "d:\\dest.txt";
//get 输出流
FileOutputStream out = new FileOutputStream(new File(destPath));
//得到channel
FileChannel fOut = out.getChannel();
//创建一个Buffer
ByteBuffer buffer = ByteBuffer.allocate(1024);
//put 进去
for (int i = 0; i < metas.length; i++) {
buffer.put(metas[i].getBytes());
}
//切换Buffer的模式到写
buffer.flip();
//写入
fOut.write(buffer);
//close
fOut.close();
out.close();


之前的例子中 可以看出通道即可以读又可以写

//得到源文件与目标文件的路径
String 	copyPath = Demo1.class.getResource("copy.txt").getPath();
String 	pastePath = Demo1.class.getResource("paste.txt").getPath();

FileInputStream ins = new FileInputStream(new File(copyPath));
FileOutputStream out = new FileOutputStream(new File(pastePath));
//从输入输出流中得到  channel
FileChannel fIns = ins.getChannel();
FileChannel fOut = out.getChannel();

//创建一个直接缓冲区!!!Buffer  大小为1024
ByteBuffer buffer = ByteBuffer.allocateDirect(1024);

while(true){
//清空缓冲区
buffer.clear();
//读输入到缓冲区
int n = fIns.read(buffer);
if(n == -1){
break;
}
// flip方法让缓冲区可以将新读入的数据写入另一个通道
buffer.flip();
// 从输出通道中将数据写入缓冲区
fOut.write(buffer);
}

System.out.println("over !");


NIO映射文件

内存映射文件 I/O

内存映射文件 I/O 是一种读和写文件数据的方法,它可以比常规的基于流或者基于通道的 I/O 快得多。

内存映射文件 I/O 是通过使文件中的数据神奇般地出现为内存数组的内容来完成的。这其初听起来似乎不过就是将整个文件读到内存中,但是事实上并不是这样。一般来说,只有文件中实际读取或者写入的部分才会送入(或者 映射)到内存中。

内存映射并不真的神奇或者多么不寻常。现代操作系统一般根据需要将文件的部分映射为内存的部分,从而实现文件系统。Java 内存映射机制不过是在底层操作系统中可以采用这种机制时,提供了对该机制的访问。

尽管创建内存映射文件相当简单,但是向它写入可能是危险的。仅只是改变数组的单个元素这样的简单操作,就可能会接修改磁盘上的文件。修改数据与将数据保存到磁盘是没有分开的。
将文件映射到内存

了解内存映射的最好方法是使用例子。在下面的例子中,我们要将一个 FileChannel (它的全部或者部分)映射到内存中。为此我们将使用 FileChannel.map() 方法。下面代码行将文件的前 1024 个字节映射到内存中:

MappedByteBuffer mbb = fc.map( FileChannel.MapMode.READ_WRITE, 0, 1024 );

map() 方法返回一个 MappedByteBuffer,它是 ByteBuffer 的子类。

因此,您可以像使用其他任何 ByteBuffer一样使用新映射的缓冲区,操作系统会在需要时负责执行行映射。

MappedByteBuffer 将文件直接映射到内存,注意这里的内存是虚拟内存,不是物理内存!

通常可以映射整个文件,如果文件比较大,可以分段进行映射,只要指定文件那个部分就可以。

通俗一点就是 Map把文件的内容映射到计算机虚拟内存的一块区域,这样就可以直接操作内存中的数据,而无需操作的时候每次都去通过IO去物理硬盘读文件

三种方式:

public abstract MappedByteBuffer map(FileChannel.MapMode mode,
long position,
long size)
throws IOException


将此通道的文件区域直接映射到内存中。

可以通过下列三种模式将文件区域映射到内存中:

只读: 试图修改得到的缓冲区将导致抛出 ReadOnlyBufferException。(MapMode.READ_ONLY)

读/写: 对得到的缓冲区的更改最终将传播到文件;该更改对映射到同一文件的其他程序不一定是可见的。 (MapMode.READ_WRITE)

专用: 对得到的缓冲区的更改不会传播到文件,并且该更改对映射到同一文件的其他程序也不是可见的;相反,会创建缓冲区已修改部分的专用副本。 (MapMode.PRIVATE)

对于只读映射关系,此通道必须可以进行读取操作;对于读取/写入或专用映射关系,此通道必须可以进行读取和写入操作。

此方法返回的已映射字节缓冲区具有的位置为零,限制和容量为size;其标记是不确定的。在缓冲区本身被作为垃圾回收之前,该缓冲区及其表示的映射关系都是有效的。

映射关系一经创建,就不再依赖于创建它时所用的文件通道。特别是关闭该通道对映射关系的有效性没有任何影响。

很多内存映射文件的细节从根本上是取决于底层操作系统的,因此是未指定的。当所请求的区域没有完全包含在此通道的文件中时,此方法的行为是未指定的。未指定是否将此程序或另一个程序对底层文件的内容或大小所进行的更改传播到缓冲区。未指定将对缓冲区的更改传播到文件的频率。

对于大多数操作系统而言,与通过普通的 readwrite 方法读取或写入数千字节的数据相比,将文件映射到内存中开销更大。从性能的观点来看,通常将相对较大的文件映射到内存中才是值得的。

//得到文件所在路径
String filePath = JavaNioMemoryMap.class.getClassLoader().getResource("a.txt").getFile();
File file = new File(filePath);
FileInputStream ins = new FileInputStream(file);
//得到通道  FileChannel
FileChannel fIns = ins.getChannel();
/*
* 内存映射文件,映射的大小是从0,到file.length()这么大
*/
MappedByteBuffer mbb = fIns.map(FileChannel.MapMode.READ_ONLY	, 0, file.length());
//读数据到 data中
byte[] data = new byte[(int) file.length()];
int index = 0;
while (mbb.hasRemaining()) {
data[index] = mbb.get();
index++;
}
//输出
System.out.println(new String(data));
//close
fIns.close();
ins.close();
小结:
有一下几种读入的方式(读入,仅仅是读入)

1 RandomAccessFile 较慢

2 FileInputStream 较慢

3 缓存读取 速度较慢

4 内存映射 速度是最快的

内存映射是在读取的时候是最快的,但是如果在使用以上操作代码的时候,执行的是写入操作则有可能是非常危险的,因为仅仅只是改变数组中的单个元素这种简单的操作,就可能直接修改磁盘上的文件,因为修改数据与将数据保存到磁盘上时一样的

所以 只是在 读取的 上面是最快的!!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: