I/O(二)—— NIO

一、NIO概述
  1. IO是阻塞的,NIO是非阻塞式的
  2. IO是面向流的(一次处理一字节,效率不高),NIO是面向块的
  3. IO是单线程的,NIO 是通过选择器来模拟多线程的

NIO在基础的IO流上发展处新的特点,分别是:内存映射技术,字符及编码,非阻塞I/O和文件锁定。
Channel(通道)和Buffer(缓冲)是NIO中的两个核心对象。

  1. Channel类似于传统IO中的流对象,其与InputStream和OutputStream最大的区别在于它提供了一个map()方法,通过该map()实现将一块数据直接映射到内存道中。
  2. Buffer可被理解为一个容器,本质是一个数组,发送到Channel中的对象和从Channel中读取数据时都必须先放到Buffer中。
二、Buffer

在这里插入图片描述
这些Buffer类都不提供构造器,而是通过static XxxBuffer allocate(int capacity)来创建一个Buffer对象。Buffer有三个重要的概念:

  1. 容量(capacity):缓冲区容量大小,表示该Buffer最大数据容量。创建后不可改变。
  2. 界限(limit):第一个不该被读出或写入的缓冲区位置索引。
  3. 位置(position):用于指明下一个可被读出或写入的缓冲区位置索引。
    在这里插入图片描述当Buffer装入数据结束后,调用flip(),将limit设置为position所在位置,将position设置为0。
    当数据输出结束后,调用clear(),将limit置为position,将position值为0,并不清除Buffer中的原有数据。
    可通过put()、get()方法向Buffer中存、取数据。
三、Channel

在这里插入图片描述

  1. 所有的Channel都是通过传统节点流的getChannel()获得

  2. Channel类似于传统的流对象,但二者的区别在于:

    1. Channel可以直接将指定的文件部分或全部直接映射成Buffer。
    2. 程序不能直接访问Channel中的数据(无论读、写),Channel只能与Buffer进行交互。
  3. map()方法:该方法用于将Channel中对应的部分映射成ByteBuffer

MappedByteBuffer map(MapMode mode, long position, long size)
四、字符集Charset
  1. 字符集用于解决二进制序列与字符之间的映射关系
  2. Charset对象的获取方法
Charset charset = Charset.forName("GBK");
  1. 获取到Charset对象后,可调用此对象的newDecoder()、newEncoder()这两个方法分别获得该字符集的解码器和编码器。
五、Selector

Selector 一般称为选择器 ,也被称为多路复用器。它是Java NIO核心组件中的一个,用于检查一个或多个NIO Channel(通道)的状态是否处于可读、可写。如此可以实现单线程管理多个channels,也就是可以管理多个网络链接。所有希望采用非阻塞方式进行通信的SelectableChannel都应注册到Selector对象中。传统阻塞式IO中,服务器需要为每个客户端链接提供一个独立线程进行处理,相较之下,Selector带来的性能提升时巨大的。

  1. Selector实例的创建
Selector s = Selector.open();
  1. 注册SelectableChannel到Selector
channel.configureBlocking(false);
//注册一个操作
SelectionKey key = channel.register(selector, Selectionkey.OP_READ);
//注册多个操作
SelectionKey key = channel.register(selector, Selectionkey.OP_READ | SelectionKey.OP_WRITE(4);

SelectableChannel对象支持阻塞和非阻塞两种模式(所有的Channel默认都是阻塞模式),必须使用非阻塞模式才可以利用非阻塞IO操作。SelectableChannel提供以下两个方法来设置和返回Channel的模式状态:

public abstract SelectableChannel configureBlocking(boolean block)throws IOException;

public abstract boolean isBlocking();

不同的SelectableChannel所支持的操作不一样,比如ServerSocketChannel就只支持OP_ACCEPT。在SelectionKey中用静态常量的方式定义了4中操作。

//任意2个、3个、4个进行按位或运算得到的结果与相加结果相等
//连接操作
SelectionKey.OP_CONNECT(8//接收操作
SelectionKey.OP_ACCEPT(16//读操作
SelectionKey.OP_READ(1//写操作
SelectionKey.OP_WRITE(4
  1. SelectionKey介绍
    一个SelectionKey键表示了一个特定的通道对象和一个特定的选择器对象之间的注册关系。
//返回SelectionKey的attachment,attachment可以在注册channel的时候指定。
key.attachment(); 
// 返回该SelectionKey对应的channel。
key.channel(); 
// 返回该SelectionKey对应的Selector。
key.selector(); 
//返回代表需要Selector监控的IO操作的bit mask
key.interestOps();
// 返回一个bit mask,代表在相应channel上可以进行的IO操作。 
key.readyOps(); 
  1. 从Selector中选择channel
    Selector中维护了3个SelectionKey的Set集合:注册到此Selector的所有Channel的key、被选中Channel的key、被取消注册关系Channel的key
	//返回所有key
    public abstract Set<SelectionKey> keys();
    //返回被选中的key
    public abstract Set<SelectionKey> selectedKeys();

在刚初始化的Selector对象中,这三个集合都是空的。 通过Selector的select()方法可以选择已经准备就绪的通道。比如你对读就绪的通道感兴趣,那么select()方法就会返回读事件已经就绪的那些通道。下面是Selector几个重载的select()方法:

	//非阻塞式,若没有选中通道则返回 0
    public abstract int selectNow() throws IOException;
	//阻塞式,阻塞到有可被选中的通道,可设置最长等待时间。返回选中数目
    public abstract int select(long timeout)throws IOException;
	//阻塞式,阻塞到有可被选中的通道,返回选中数目
    public abstract int select() throws IOException;
  1. 停止选择的方法
    执行选择的过程中可能会发生阻塞,使用以下方法可以使阻塞线程立即返回
	该方法使得选择器上的第一个还没有返回的选择操作立即返回。
	如果当前没有进行中的选择操作,那么下一次对select()方法的一次调用将立即返回。
	public abstract Selector wakeup();
	该方法使得任何一个在选择操作中阻塞的线程都被唤醒(类似wakeup()),
	同时使得注册到该Selector的所有Channel被注销,所有的键将被取消,但是Channel本身并不会关闭。
    public abstract void close() throws IOException;
六、Java7的NIO.2
  1. 提供了Path类,优化了传统File类功能有限且所提供方法性能不高的缺陷。
  2. 提供了Files和Paths两个工具类
  3. 使用FileVisitor遍历文件和目录
public interface FileVisitor<T> {
    
    
	//访问子目录之前触发事件
    FileVisitResult preVisitDirectory(T dir, BasicFileAttributes attrs)
        throws IOException;
	//访问file文件时触发事件
    FileVisitResult visitFile(T file, BasicFileAttributes attrs)
        throws IOException;
	//访问文件失败时触发事件
    FileVisitResult visitFileFailed(T file, IOException exc)
        throws IOException;
	//访问子目录之后触发事件
    FileVisitResult postVisitDirectory(T dir, IOException exc)
        throws IOException;
}

以上四个方法都返回一个FileVisitResult对象,他是一个枚举类,代表后续行为

public enum FileVisitResult {
    
    
    //继续访问
    CONTINUE,
    //中止范围跟
    TERMINATE,
    //继续访问,但不访问该文件或跳过该目录的子目录树
    SKIP_SUBTREE,
    //继续访问,但不访问该文件或目录的兄弟文件或目录
    SKIP_SIBLINGS;
}
  1. 使用WatchService来监控文件变化
    NIO.2的Path类提供如下方法来监听文件系统的变化
    //用watcher监听变化。events参数指定要监听哪些类型的事件。
    WatchKey register(WatchService watcher, WatchEvent.Kind<?>... events)throws IOException;

	WatchKey register(WatchService watcher,WatchEvent.Kind<?>[] events,WatchEvent.Modifier... modifiers)
        throws IOException;

注册完成后,可调用WatchService的如下三个方法来获取变化事件

	//获取下一个WatchKey,如果没发生,则返回null
	WatchKey poll();
	//等待指定的时间,返回下一个WatchKey,如果一直没事件发生则返回null
    WatchKey poll(long timeout, TimeUnit unit) throws InterruptedException;
    //获取下一个WatchKey,如果没发生就一直等待
    WatchKey take() throws InterruptedException;

猜你喜欢

转载自blog.csdn.net/qq_42451178/article/details/113133334
今日推荐