java.nio体系介绍
简介
nio 是New IO 的简称,在jdk1.4 里提供的新api 。Sun 官方标榜的特性如下: 为所有的原始类型提供(Buffer)缓存支持。字符集编码解码解决方案。 Channel :一个新的原始I/O 抽象。 支持锁和内存映射文件的文件访问接口。 提供多路(non-bloking) 非阻塞式的高伸缩性网络I/O 。请查看后文资料 io发展历史
学习目的
重点了解整个nio的核心内容以及体系结构;次级目标:简单使用。
一个简单比喻看java.nio的核心特征
假如我们要通过io完成一个对文件的操作。我们可以把它想象成煤矿。通道是一个包含煤层(数据)的矿藏,而缓冲器则是派送到矿藏的卡车。卡车满载而归,我们再从卡车上获得煤炭。也就是说,我们并没有直接和通道交互;我们只是和缓冲器交互,并把缓冲器派送到通道。通道要么从缓冲器获得数据,要么向缓冲器发送数据。(thinking in java)
java.nio的特点
通道(Channel),一种新的原生I/O抽象概念
Channel(通道):NIO把它支持的I/O对象抽象为Channel。它模拟了通信连接,类似于原I/O中的流(Stream),用户可以通过它读取和写入数据。目前已知的实例类有SocketChannel、ServerSocketChannel、DatagramChannel、FileChannel等。
原生类型数据缓冲(Buffer)
Buffer(缓冲区):Buffer是一块连续的内存区域,一般作为Channel收发数据的载体出现。所有数据都通过Buffer对象来处理。可以查看api树形结构图。
通过多路复用、非阻塞的I/O能力实现可伸缩的服务器架构【重点】
传统阻塞方式的Socket编程,在读取或者写入数据时,TCP程序会阻塞直到客户端和服务端成功连接。阻塞方式会影响程序性能,JDK4之后的NIO引入了非阻塞方式的Socket编程,非阻塞方式的Socket编程主要是使用Socket通道和Selector通道选择器,将Socket通道注册到通道选择器上,通过通道选择器选择通道已经准备好的事件的进行相应操作。请查看演示代码:com.gist.nio.nonblock
字符集的编码器和解码器【讲解】
java.nio.charset是用来在字节和 Unicode 字符之间转换的 charset、解码器和编码器。(百度百科)
该方法一般不直接用,但是很重要。我们一般通过其它方法间接使用chaeset方法,请查看String(byte[] bytes, Charset charset)、InputStreamReader(InputStream in, Charset cs) 、OutputStreamWriter(OutputStream out, Charset cs) 等。请查看com.gist.charset.CharSetDemo
支持锁和内存映射文件的文件访问接口。【讲解】
内存映射文件允许我们创建和修改那些因为太大而不能放入内存的文件。有了内存映射文件,我们就可以假定整个文件都放到在内存中(其实只有一部分文件放入了内存,文件的其它部分被交换了出去。),而且可以完全把它当做非常大的数组来访问。这种方法极大地简化了用于修改文件的代码。(thingking in java)
文件加锁机制,它允许我们同步访问某个作为共享资源的文件。不过竞争同一个文件的两个线程可能不在同一个jvm上;或者一个是java线程,另外一个是操作系统中的某个本地线程。文件锁对于其它操作系统的进程是可见的,因为java的文件加锁直接映射到os的加锁工具。(thingking in java)
请查看演示代码com.gist.mapandlock
java.nio与java.io的主要区别
阻塞(io)与非阻塞io(nio)
阻塞io(blockIO):类似于Socket聊天小程序,当客户端连接到服务器端之后,读写流一直处于等待状态,当有内容传递过来时进行输入和输出。简而言之,一个连接对应一个线程。
非阻塞io(non-bloking IO):简单来说,就是少量的线程对应多个连接。实现策略是:缓冲区+通道+选择器。
*请查看演示代码:com.gist.nio.nonblock*
请查看后文资料 阻塞、非阻塞、同步、异步理解
面向流(io)与面向缓冲(nio)
面向流(io)
/** * @author 高远 date time:2016-3-3 下午9:53:27 * * */ public class IOTest { public final static String INPUTPATH = "e://1.avi";// 需要读的文件 public final static String OUTPUTPATH = "e://b.avi";// 需要写的文件 @Test public void inputProperties() throws Exception { long start = System.currentTimeMillis(); File file = new File(INPUTPATH); InputStream in = new FileInputStream(file); OutputStream out = new FileOutputStream(OUTPUTPATH); int read; byte[] chars = new byte[1024]; while ((read = in.read(chars)) != -1) { out.write(chars); chars = new byte[1024]; } in.close(); long end = System.currentTimeMillis(); System.out.println("普通copy方法,耗时:" + (end - start) + "ms"); } }
面向缓冲(nio)
/**
- @author 高远 邮箱:wgyscsf@163.com 博客 http://blog.csdn.net/wgyscsf
- 编写时期 2016-3-21 上午10:00:06
*/
// NIO读数据
public class NIOTest {
public final static String INPUTPATH = IOTest.INPUTPATH;// 需要读的文件
public final static String OUTPUTPATH = IOTest.OUTPUTPATH;// 需要写的文件 //@Test public void copyNew() { long start = System.currentTimeMillis(); try { File srcFile = new File(INPUTPATH); File distFile = new File(OUTPUTPATH); if (distFile.exists()) { distFile.delete(); } FileInputStream fin = new FileInputStream(srcFile); FileOutputStream fout = new FileOutputStream(distFile); FileChannel inChannel = fin.getChannel(); FileChannel outChannel = fout.getChannel(); int ByteBufferSize = 1024 * 100; ByteBuffer buff = ByteBuffer.allocate(ByteBufferSize); while (inChannel.read(buff) > 0) { buff.flip(); if (inChannel.position() == inChannel.size()) {// 判断是不是最后一段数据 int lastRead = (int) (inChannel.size() % ByteBufferSize); byte[] bytes = new byte[lastRead]; buff.get(bytes, 0, lastRead); outChannel.write(ByteBuffer.wrap(bytes)); buff.clear(); } else { outChannel.write(buff); buff.clear(); } } outChannel.close(); inChannel.close(); fin.close(); fout.close(); } catch (Exception e) { e.printStackTrace(); } long end = System.currentTimeMillis(); System.out.println("new IO 方法,耗时:" + (end - start) + "ms"); }
}
java.nio与java.io的性能对比
请查看演示代码:com.gist.bufferandinputstream
请查看后文资料 读取文件速度问题
资料
io发展历史
Java NIO : 同步非阻塞,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。
Java AIO(NIO.2) : 异步非阻塞,服务器实现模式为一个有效请求一个线程,客户端的I/O请求都是由OS先完成了再通知服务器应用去启动线程进行处理,
NIO方式适用于连接数目多且连接比较短(轻操作)的架构,比如聊天服务器,并发局限于应用中,编程比较复杂,JDK1.4开始支持。
AIO方式使用于连接数目多且连接比较长(重操作)的架构,比如相册服务器,充分调用OS参与并发操作,编程比较复杂,JDK7开始支持
I/O属于底层操作,需要操作系统支持,并发也需要操作系统的支持,所以性能方面不同操作系统差异会比较明显。另外NIO的非阻塞,需要一直轮询,也是一个比较耗资源的。所以出现AIO
阻塞、非阻塞、同步、异步理解
阻塞:阻塞调用是指调用结果返回之前,当前线程会被挂起。函数只有在得到结果之后才会返回。
非阻塞:非阻塞和阻塞的概念相对应,指在不能立刻得到结果之前,该函数不会阻塞当前线程,而会立刻返回。
同步:所谓同步,就是在发出一个功能调用时,在没有得到结果之前,该调用就不返回。
异步:异步的概念和同步相对。当一个异步过程调用发出后,调用者不能立刻得到结果。实际处理这个调用的部件在完成后,通过状态、通知和回调来通知调用者。
java.nio在电信行业的应用
http://www.infoq.com/cn/articles/practice-of-java-nio-communication-framework?utm_source=tuicool
读取文件速度问题
NIO 与传统IO的测试,我也做过。两者在普通文件的读取时,速度几乎没有差异。
更有趣的是,如果传统IO,使用的得当(比如使用BufferedInputStream、BufferedReader
),是很快的。如果NIO使用不当(比如,我用MappedByteBuffer,将文件映射到内存,Buffer的大小是文件的总长度43M),速度是传统IO的二十分之一。
引用IBM官方教程中的一句话,来解释:
引用
在 JDK 1.4 中原来的 I/O 包和 NIO 已经很好地集成了。 java.io.* 已经以 NIO 为基础重新实现了,所以现在它可以利用 NIO 的一些特性。例如, java.io.* 包中的一些类包含以块的形式读写数据的方法,这使得即使在更面向流的系统中,处理速度也会更快。
可以看到,1.4后的IO经过了集成。所以NIO的好处,集中在其他特性上,而非速度了:
1、分散与聚集读取
2、文件锁定功能
3、网络异步IO
推荐资料
API【推荐】
ibm官方教程【推荐】
http://www.ibm.com/developerworks/cn/education/java/j-nio/j-nio.html#icomments