我们都知道IO流传输,其实IO模型有很多,例如BIO、NIO、AIO等,传统的IO都是同步的。
IO简介
IO为各种流操作
IO操作分类 I
IO操作分类 II
其中,输入流可以为InputStream和Reader,分别为字节流和字符流,对应地,输出流为OutputStream和Writer,具体的使用在此不详述。
NIO简介
NIO是IO模型中后推出的新IO模型。NIO并不一定是多线程的,但是NIO是多管道的,利用缓冲作为中间介质进行数据传输,运用的其实是多路复用技术,它恰恰是通过减少线程数量从而减少上下文的频繁切换,提高性能。
一些概念
Channel:通道,相当于一个连接,不能直接输出数据,只能与Buffer交换数据
Selector:选择器
Buffer:数据的缓冲区
基本的读取流程
使用Buffer读写数据一般遵循以下四个步骤:
写入数据到Buffer
调用flip()方法
从Buffer中读取数据
调用clear()方法或者compact()方法
常用对象
Buffer
Buffer只能写或是读,调用flip()方法可以使buffer从写模式切换为读模式,而调用clear()会清空整个缓存区,调用compact()会清空已读区域,并将指针指向未读的起点,限制limit为position。
Buffer有三个基本的参数:
capacity:以byte为单位的容量
position:指针位置,当调用flip()后,该值置为0,也是初始位置
limit:指出下一次操作(无论读写)所能操作的最大数据量
Selector
在nio中,一个线程可以处理多个通道,通过Selector可以处理多个通道,从某种逻辑上说它类似于连接池,基本使用方式:
//创建选择器 Selector selector = Selector.open(); //注册通道 channel.configureBlocking(false); SelectionKey key = channel.register(selector,Selectionkey.OP_READ);
这里注册通道方法使用了两个参数,其中第二个参数是监听事件,它包含Connect、Accept、Read、Write,通过设定这些时间可以获取指定状态的通道。Selector只能注册那些非阻塞的通道,所以不能设置为非阻塞状态的FileChannel不能被注册进Selector中,注册返回的SelectionKey记录了这个选择器的一些信息和状态,同时也用来访问Channel和Selector。
我们通过Selector对象可以获得符合标准的通道。直接调用select()方法,它会阻塞直到有通道进入就绪状态,所以当我们调用这个方法并获取到了返回的结果,这个结果代表自上次执行select之后新出现的就绪通道数量,就说明已经有至少一个符合要求的通道就绪了,相应地,还有方法selectNow不会阻塞(有可能返回0),以及select(long timeout)会阻塞最多timout(毫秒)时间。
通过selector.selectedKeys()方法可以返回一个Set<SelectionKey>对象,记录了当前已经注册的通道,以下的代码可以获取当前Selector中的Channel状态们,调用key.channel()可以获得通道:
Set selectedKeys = selector.selectedKeys(); Iterator keyIterator = selectedKeys.iterator(); while(keyIterator.hasNext()) { SelectionKey key = keyIterator.next(); if(key.isAcceptable()) { // a connection was accepted by a ServerSocketChannel. SocketChannel clientChannel = ((ServerSocketChannel) key.channel()).accept(); } else if (key.isConnectable()) { // a connection was established with a remote server. } else if (key.isReadable()) { // a channel is ready for reading } else if (key.isWritable()) { // a channel is ready for writing } keyIterator.remove(); }
常用操作
分散和聚集
直接上代码
//Scattering Reads 逐个从Channel读 ByteBuffer header = ByteBuffer.allocate(128); ByteBuffer body = ByteBuffer.allocate(1024); ByteBuffer[] bufferArray = { header, body }; channel.read(bufferArray) //Gathering Writes 逐个写入Channel ByteBuffer header = ByteBuffer.allocate(128); ByteBuffer body = ByteBuffer.allocate(1024); ByteBuffer[] bufferArray = { header, body }; channel.write(bufferArray);
通道间传输
如果两个Channel之间有一个是FileChannel,可以通过通道间传输向文件写入数据,
//transferFrom 将数据写入fileChannel RandomAccessFile fromFile = new RandomAccessFile("fromFile.txt", "rw"); FileChannel fromChannel = fromFile.getChannel(); RandomAccessFile toFile = new RandomAccessFile("toFile.txt", "rw"); FileChannel toChannel = toFile.getChannel(); long position = 0; long count = fromChannel.size(); toChannel.transferFrom(position, count, fromChannel); //transferTo 将数据从fileChannel读 RandomAccessFile fromFile = new RandomAccessFile("fromFile.txt", "rw"); FileChannel fromChannel = fromFile.getChannel(); RandomAccessFile toFile = new RandomAccessFile("toFile.txt", "rw"); FileChannel toChannel = toFile.getChannel(); long position = 0; long count = fromChannel.size(); fromChannel.transferTo(position, count, toChannel);
特性
NIO模型特性:创建缓冲区,请求通道(Channel),利用缓冲区(Buffer)读写数据,同时,不同于IO,NIO进行数据操作时对应数据流是非阻塞的。
在使用IO模型时,经常这样使用:
InputStream input ///* ; BufferedReader reader = new BufferedReader(new InputStreamReader(input)); String nameLine = reader.readLine();
当引入NIO之后:
FileInputStream fin = new FileInputStream("c:\\test.txt"); FileChannel fc = fin.getChannel(); // 创建通道 ByteBuffer buffer = ByteBuffer.allocate(1024); //创建缓冲区 fc.read(buffer); // 读取数据到缓冲区 buffer.flip(); //固定缓冲区,将写数据变成读 while (buffer.remaining() > 0) { byte b = buffer.get(); System.out.print(((char)b)); } fin.close();
NIO运行原理
参考链接:并发编程网-Java NIO系列教程