一、序言
关于NIO 相关介绍也很多了,这里主要目的还是通过自己的学习,分享一部分自己的理解,计划从基本概念到原理、到使用、到一些源码底层的分析,也回顾和总结自己的学习历程,今天先介绍概念。
二、NIO介绍
NIO 从JDK1.4 提出的,介绍是为了弥补IO的不足,提供了更高效的方式,提出一个通道(channel)的概念。源于IO的不足,还在在于它始终以流的形式对数据的传输和接受,当然NIO也差不多,但是NIO 提供了一种内存映射数据的方式,
简单的理解是:IO 原来是用固定的水管传输和接受水源,NIO可以提供一种用桶的方式传输和接受水源。比如在取水过程中,IO可以让接水,NIO可以直接一次打满一桶水,从效率上看,很明显的,当然这仅仅是一个好处,其他的我们后面解释。
三、通道和缓冲区
缓冲区(buffer):这个很好理解,即使IO也有这个概念,就就像我们读、写文件的时候,不会拿到一个字节,就对文件进行读、写操作,一般情况会建立一个缓存区数组,比如new byte[1024*4],然后将缓冲区的数据一次操作。在NIO中这里的原理其实一样的,可能细节实现上有些区别。
通道(channle): 这玩意儿,它需要从缓冲区获取数据,缓冲区也可以从通道里面提取数据,相当于我另外开辟了一个空间来存放我的数据,而且是双向的,对里面数据的掌控性也更好,好吧, 这些都是空话,实践出真知,我们后面实践的时候再对它进行细致的描述。
四、JDK 和 代码示例
全理论化的东西,是很烦人,即使写得很大白话,还是代码看着亲切。
1.先看看buffer 缓冲区: 这个其实也是装老的byte[] ,先看看我们将文件流放到byte 数组的写法区别
// 这是原始方式之一
FileInputStream is = new FileInputStream(name);
// 这个区间可以自己定义
byte[] buff = new byte[is.available()];
is.read(buff);
return buff;
// 这是新方式
FileInputStream f = new FileInputStream(name);
// 获得通道,这里用了API 没公布的一些类,组合而成,有时间可以去看 // 看源码
FileChannel channel = f.getChannel();
// 创建一个缓冲区
ByteBuffer buffer = ByteBuffer.allocate(f.available());
// 从通道读取放入缓冲区
channel.read(buffer);
// 注意,这里如果是分配的直接缓冲区 ,此方法会出错的,可以看源码
byte[] bytes = buffer.array();
上面的例子,其实你也不知道有啥好处,仅仅熟悉一下必要的步骤:通道<---> 缓冲区
下面我们来看看文件复制的操作:
// 原始方式的文件复制
public static void copyFile(String orgFile,String targetFile) throws Exception{
FileInputStream in = new FileInputStream(orgFile);
FileOutputStream out = new FileOutputStream(targetFile);
byte[] buff = new byte[1024*8];
int len = 0;
while((len = in.read(buff)) != -1){
out.write(buff,0,len);
}
}
// 这是利用通道 缓冲区 的方式,和上面差不多
public static void copyFile2(String orgFile,String targetFile) throws Exception{
FileInputStream in = new FileInputStream(orgFile);
FileOutputStream out = new FileOutputStream(targetFile);
FileChannel orgChannel = in.getChannel();
FileChannel tarChannel = out.getChannel();
ByteBuffer buffer = ByteBuffer.allocate(1024*8);
int len = 0;
while((len = orgChannel.read(buffer)) != -1){
buffer.flip();
tarChannel.write(buffer);
buffer.clear();
}
}
// 这个是直接用API 的方式
public static void copyFile3(String orgFile,String targetFile) throws Exception{
FileInputStream in = new FileInputStream(orgFile);
FileOutputStream out = new FileOutputStream(targetFile);
FileChannel orgChannel = in.getChannel();
FileChannel tarChannel = out.getChannel();
orgChannel.transferTo(0, orgChannel.size(), tarChannel);
}
上面测试我用了一个大于JVM 限制的文件复制,结果是最后一种最快啦~。~(小文件就算了)
关于通道复制速度的解释:http://www.ibm.com/developerworks/cn/java/j-zerocopy/ 很详细
2.关于通道这个东西我的理解是:就像给我们数据源和地点之间加了一个大的用于传输的通道,这不是简单的让字节流通过的通道,而是里面允许我们做更多的检测、加速、返回等等操作的通道,后面分析源码的时候再体会吧。
关于缓冲区要说到的几个元素和方法,比如我们刚才用到的flip和clear。首先要理解这个缓冲区其实就是一个byte[] 数组的空间,里面有这几个元素:
Capacity:这表示容量,也就是说我数组的最大长度
position: 相当于指针,当我们操作数组里面元素的时候,这个会跟着移动,记录我们的位置,方便我们记录下次要用数据。
mark:标记位置,和事务的回滚位置差不多,相当于我们在这个位置记录一下,下一次我可以返回到这个位置干事。
limit :我们只能读操作到位置,比如我10长度的数组,只有前面6个又数据,那么我就设置只能操作前面6个有效的。
关于基本的东西可以看看源码,很容易,也可以看看这文章:
http://www.ibm.com/developerworks/cn/education/java/j-nio/section5.html
小结:
1.这里仅仅介绍下NIO 的使用基本情况,没有深入,它的优势还在于它的不阻塞模式,和Selector管理模式,后面会根据原理和源码分析。