IO与NIO

  created  by  鱼鱼 {{tag}}
创建于 2019年01月27日 10:38:25 最后修改于 2019年05月11日 15:30:08
评论区
评论
{{comment.creator}}
{{comment.createTime}} {{comment.index}}楼
评论

IO与NIO

IO与NIO

    我们都知道IO流传输,其实IO模型有很多,例如BIO、NIO、AIO等,传统的IO都是同步的。

IO简介

    IO为各种流操作

按操作方式分类结构图:

IO操作分类 I

按操作对象分类结构图

IO操作分类 II

    其中,输入流可以为InputStream和Reader,分别为字节流和字符流,对应地,输出流为OutputStream和Writer,具体的使用在此不详述。

    NIO简介

    NIO是IO模型中后推出的新IO模型。NIO并不一定是多线程的,但是NIO是多管道的,利用缓冲作为中间介质进行数据传输,运用的其实是多路复用技术,它恰恰是通过减少线程数量从而减少上下文的频繁切换,提高性能。

    一些概念

    基本的读取流程

使用Buffer读写数据一般遵循以下四个步骤:

  1. 写入数据到Buffer

  2. 调用flip()方法

  3. 从Buffer中读取数据

  4. 调用clear()方法或者compact()方法

    常用对象

    Buffer

    Buffer只能写或是读,调用flip()方法可以使buffer从写模式切换为读模式,而调用clear()会清空整个缓存区,调用compact()会清空已读区域,并将指针指向未读的起点,限制limit为position。

    Buffer有三个基本的参数:

    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();

image | left | 559x394

NIO运行原理

参考链接:并发编程网-Java NIO系列教程


2019-05-11鱼鱼

{{commentTitle}}

评论   ctrl+Enter 发送评论