一、介绍java中的IO
java中的IO分许多种
具体介绍请往这走http://blog.51cto.com/stevex/1284437
java IO 中的NIO的英译名为(new IO ) 他是的出现时在JDK1.4版本才有的。
NIO 的它实则是一种非阻塞的IO,它是一种基于缓存区的IO读写。
以前的BIO是一种基于流的读写。每次读写都消耗一个字节数据。而NIO则是一种面向块的读写。每次读写都消耗一个数据块。在操作的速度上要比BIO快许多。
二、使用NIO
首先介绍一下NIO的必须要知道的几个词汇
1.通道 2.缓冲区
通道
和 缓冲区
是 NIO 中的核心对象,几乎在每一个 I/O 操作中都要使用它们。
通道是对原 I/O 包中的流的模拟。到任何目的地(或来自任何地方)的所有数据都必须通过一个 Channel 对象。一个 Buffer 实质上是一个容器对象。发送给一个通道的所有对象都必须首先放到缓冲区中;同样地,从通道中读取的任何数据都要读到缓冲区中。
NIO同时支持多路IO复用。(多个通道连接到同一个缓冲区)
NIO中程序操作的是缓冲区。文件的读取步骤将缓冲区中的数据读取全部读取到。然后将缓冲区中的缓存内容清除。NIO中文件写入操作则是将缓存中的缓存内容写入到文件中去。
· 以下是一个基于BIO的简单文件拷贝的实现。
package com.demo.io.nio; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; public class FileChannelTest { public static void main(String[] args) throws IOException { FileInputStream fis = new FileInputStream("C:\Users\Desktop\test.jsp"); FileChannel foc = new FileOutputStream("C:\Users\Desktop\test.txt").getChannel(); FileChannel fic = fis.getChannel(); //创建一个缓冲区大小为48的ByteBuffer ByteBuffer bb = ByteBuffer.allocate(2); int br = 0; while((br=fic.read(bb))!= -1) { //bb.flip() 的调用,首先读取数据到Buffer,然后反转Buffer,接着再从Buffer中读取数据 bb.flip(); //告诉当前位置和极限之间是否存在任何元素(此缓冲区中有元素) while(bb.hasRemaining()) { foc.write(bb); } System.out.println(); //缓存满了就会清理缓存 bb.clear(); } fic.close(); fis.close(); } }
三、简单的文件操作
操作流程图:
代码:
public class NIOFileChannel { public static void main(String[] args) throws IOException { String str = "hello world"; // 创建一个输出流 FileOutputStream fileOutputStream = new FileOutputStream("C:\file\file01.txt"); // 使用输出流获取到对应的FileChannel FileChannel fileChannel = fileOutputStream.getChannel(); // 创建一个缓冲区 ByteBuffer byteBuffer = ByteBuffer.allocate(1024); // 将要写入的数据放入byteBuffer中 byteBuffer.put(str.getBytes()); // 反转ByteBuffer byteBuffer.flip(); // 将缓冲区的数据写入通道中 fileChannel.write(byteBuffer); fileChannel.close(); fileOutputStream.close(); } }
public class NIOFileChannelRead { public static void main(String[] args) throws IOException { // 创建文件输入流 FileInputStream fileInputStream = new FileInputStream("C:\file\file01.txt"); FileChannel fileChannel = fileInputStream.getChannel(); // 创建缓冲区 ByteBuffer byteBuffer = ByteBuffer.allocate(4); while (true) { // 将数据读取到fileChannel中 int read = fileChannel.read(byteBuffer); if (read == -1) { break; } // 这里如果缓冲区满了需要清空一下才能继续读 byteBuffer.clear(); // 输出数据内容 System.out.println(new String(byteBuffer.array())); } fileChannel.close(); fileInputStream.close(); } }
1 public class NIOFileChannelCopy { 2 public static void main(String[] args) throws IOException { 3 // 创建相关流 4 FileInputStream fileInputStream = new FileInputStream("C:\file\file01.txt"); 5 FileOutputStream fileOutputStream = new FileOutputStream("C:\file\file02.txt"); 6 // 获取相关管道 7 FileChannel sourceCh = fileInputStream.getChannel(); 8 FileChannel destCh = fileOutputStream.getChannel(); 9 10 // 使用transferForm完成拷贝 11 destCh.transferFrom(sourceCh, 0, sourceCh.size()); 12 sourceCh.close(); 13 destCh.close(); 14 } 15 }
四、零拷贝
大家都说NIO的效率很高,使用到了零拷贝,至于什么是零拷贝,我们需要先探究一下Java文件读取机制。
零拷贝(Zero-copy)技术指在计算机执行操作时,CPU 不需要先将数据从一个内存区域复制到另一个内存区域,从而可以减少上下文切换以及 CPU 的拷贝时间。它的作用是在数据报从网络设备到用户程序空间传递的过程中,减少数据拷贝次数,减少系统调用,实现 CPU 的零参与,彻底消除 CPU 在这方面的负载。实现零拷贝用到的最主要技术是 DMA 数据传输技术和内存区域映射技术。
简单点讲就是零拷贝就是没有CPU拷贝
BIO拷贝方式(经过4次拷贝,三次态的切换)
1. DMA拷贝:直接内存拷贝,不使用CPU
2.CPU拷贝:讲内存中的数据经过CPU拷贝到用户缓冲区
3.CPU拷贝:讲数据拷贝到Socket buffer中
4.DMA拷贝:将数据拷贝到我们的协议栈
mmap拷贝方式(经过三次拷贝,三次态的切换)
1. DMA 拷贝
2.用户缓冲区与内核缓冲区存在内存映射不会进行拷贝
3.CPU拷贝到socket buffert
4.socket buffer DMA拷贝到协议栈
sendFile(经过两次次拷贝,两次态的切换)
Linux 2.1 提供
1.DMA拷贝
2.内核缓冲区拷贝到sockert buffer
3.DMA拷贝到协议栈
Linux 2.4提供
1.DMA拷贝
2.内核缓冲区直接DMA拷贝到协议栈
五、后记
个人觉得Java NIO 的API 是非常难用的。这里我推荐大家使用Netty。Netty封装的API相对来说好用很多。