Java NIO详解

Java NIO

1、简介

Java NIO( New IO/Non-blocking IO) 是从Java 1.4版本开始引入的一个新的IO API,可以替代标准的Java IO API。NIO与原来的IO有同样的作用和目的,但是使用的方式完全不同, NIO支持面向缓冲区的、基于通道的IO操作。 NIO将以更加高效的方式进行文件的读写操作。

  1. 同步/异步

    客户端在请求数据的过程中,能否做其他事情?能:异步。不能:同步。

  2. 阻塞/非阻塞

    客户端与服务端是否从头到尾始终有一个持续连接,以至于占用了通道,不让其它客户端连接。(这里客户端、服务端只是用于举例)

  3. BIO(Blocking IO)

    同步阻塞IO,即传统的IO方式。这种方式是面向流的。数据的读写必须阻塞在一个线程内等待其完成。这种方式在对于少量的读写请求时,可以使用多线程解决,即适用于低负载,低并发的应用程序。但面对大量的连接请求时,这种方式无法处理。因此,需要引入NIO。

  4. NIO(New IO/Non-blocking IO)

    同步非阻塞IO。这种方式是面向缓冲区的。对于BIO提供的Socket和ServerSocket,NIO提供了SocketChannel和ServerSocketChannel。适用于高负载、高并发的应用程序。

  5. AIO(Asynchronous IO)

    异步非阻塞IO。异步IO是NIO的改进版,基于事件和回调机制实现,即应用在操作后会返回,不会堵在那里,当后台处理完成后,操作系统再通知相应线程进行后续操作。这种方式会导致在执行别的方法时突然接到通知,需要保存当前的执行进度,然后再跳转到之前的操作,导致方法间来回切换。目前应用不广泛。

2、缓冲区

  1. 缓冲区

    用于存储数据,在通道中传送。类似于火车,在发送发装数据,在接收处收数据。

    在java中表示 一个用于特定基本数据类型的容器。由 java.nio 包定义的,所有缓冲区都是 Buffer 抽象类的子类 。 数据是从通道读入缓冲区,从缓冲区写入通道中的。

  2. Buffer常用子类

    ByteBuffer、CharBuffer、ShortBuffer、IntBuffer、LongBuffer、FloatBuffer、DoubleBuffer

  3. 获取Buffer对象

    static XxxBuffer allocate(int capacity) : 创建一个容量为 capacity 的 XxxBuffer 对象

  4. Buffer相关概念

    • 容量 (capacity) : 表示 Buffer 最大数据容量,缓冲区容量不能为负,创建后不能更改。
    • 限制 (limit): 第一个不应该读取或写入的数据的索引,即位于 limit 后的数据不可读写。缓冲区的限制不能为负,并且不能大于其容量。
    • 位置 (position): 下一个要读取或写入的数据的索引。缓冲区的位置不能为负,并且不能大于其限制
    • 标记 (mark)与重置 (reset): 标记是一个索引,通过 Buffer 中的 mark() 方法指定 Buffer 中一个特定的 position,之后可以通过调用 reset() 方法恢复到这个 position.
    • 标记、 位置、 限制、 容量遵守以下不变式: 0 <= mark <= position <= limit <= capacity
  5. 数据操作

    • get() :读取单个字节

      get(byte[] dst):批量读取多个字节到 dst 中

      get(int index):读取指定索引位置的字节(不会移动 position)

    • put(byte b):将给定单个字节写入缓冲区的当前位置

      put(byte[] src):将 src 中的字节写入缓冲区的当前位置

      put(int index, byte b):将指定字节写入缓冲区的索引位置(不会移动 position)

  6. 直接与非直接缓冲区

    应用程序不能直接从物理磁盘中读取数据,先要将数据读到内核地址空间中,再复制到用户地址空间,再读到应用程序,写数据同理。

    在上述过程中,复制操作可以省略,因此引入直接缓冲区即物理内存映射文件,不用再进行复制操作。

    非直接缓冲区:通过 allocate() 方法分配缓冲区,将缓冲区建立在 JVM 的内存中。

    直接缓冲区:通过 allocateDirect() 方法分配直接缓冲区,将缓冲区建立在物理内存中。可以提高效率。

3、通道

​ 打开到IO设备(套接字、文件等)的连接,负责在其中传输数据。类似于铁轨,用于连接两方的线路。

​ 从计算机原理上理解,传统的IO接口需要CPU负责,当有大量的IO接口中需要传输数据时,占用大量的CPU资源。后来引入DMA,直接访问内存,不需要CPU过多干预,这种方式在有大量的数据请求时,会需要大量的DMA总线,这时会造成总线冲突。因此后来又引入了通道,通道是一个独立的处理器,拥有一套自己的命令,完全独立出来用于IO操作。

  1. Channel常见实现类

    • FileChannel:用于读取、写入、映射和操作文件的通道。
    • DatagramChannel:通过 UDP 读写网络中的数据通道。
    • SocketChannel:通过 TCP 读写网络中的数据。
    • ServerSocketChannel:可以监听新进来的 TCP 连接,对每一个新进来的连接都会创建一个 SocketChannel。
  2. 获取通道

    获取通道的一种方式是对支持通道的对象调用getChannel() 方法。支持通道的类如下:

    本地IO:

    • FileInputStream/FileOutputStream
    • RandomAccessFile

    网络IO:

    • DatagramSocket
    • Socket
    • ServerSocket

    jdk1.7之后:

    • 获取通道的其他方式是使用 Files 类的静态方法 newByteChannel() 获取字节通道。
  • 通过通道的静态方法 open() 打开并返回指定通道。
  1. transferFrom() 函数: 将数据从源通道传输到其他 Channel 中。

  2. transferTo() 函数: 将数据从源通道传输到其他 Channel 中。

4、分散与聚集

  1. 分散读取(Scattering Reads):将通道中的数据分散到多个缓冲区中
  2. 聚集写入(Gathering Writes):将多个缓冲区中的数据聚集到通道中

5、字符集

  1. 编码:字符串 -> 字节数组

  2. 解码:字节数组 -> 字符串

6、选择器(Selector)

  1. 选择器( Selector) 是 SelectableChannle 对象的多路复用器, Selector 可以同时监控多个 SelectableChannel 的 IO 状况,也就是说,利用 Selector可使一个单独的线程管理多个 Channel。 Selector 是非阻塞 IO 的核心。

  2. SelectionKey: 表示 SelectableChannel 和 Selector 之间的注册关系。每次向选择器注册通道时就会选择一个事件(选择键)。 选择键包含两个表示为整数值的操作集。操作集的每一位都表示该键的通道所支持的一类可选择操作 。

    • 当调用 register(Selector sel, int ops) 将通道注册选择器时,选择器对通道的监听事件,需要通过第二个参数 ops 指定。

    • 可以监听的事件类型( 可使用 SelectionKey 的四个常量表示):

      读 : SelectionKey.OP_READ ( 1)

      写 : SelectionKey.OP_WRITE ( 4)

      连接 : SelectionKey.OP_CONNECT ( 8)

      接收 : SelectionKey.OP_ACCEPT ( 16)

    • 若注册时不止监听一个事件,则可以使用“位或”操作符连接

7、SocketChannel

  1. Java NIO中的SocketChannel是一个连接到TCP网络套接字的通道。

  2. 操作步骤:

    打开 SocketChannel

    读写数据

    关闭 SocketChannel

  3. Java NIO中的 ServerSocketChannel 是一个可以监听新进来的TCP连接的通道,就像标准IO中的ServerSocket一样。

8、DatagramChannel

  1. Java NIO中的DatagramChannel是一个能收发UDP包的通道。

  2. 操作步骤:

    打开 DatagramChannel

    接收/发送数据

9、管道(Pipe)

  1. Java NIO 管道是2个线程之间的单向数据连接。Pipe有一个source通道和一个sink通道。数据会被写到sink通道,从source通道读取。

10、NIO2

  1. 概念

JDK 7 对NIO进行了极大的扩展,增强了对文件处理和文件系统特性的支持,称为 NIO.2。因为 NIO 提供的一些功能, NIO已经成为文件处理中越来越重要的部分。

  1. Path 与 Paths
  • java.nio.file.Path 接口代表一个平台无关的平台路径,描述了目录结构中文件的位置。

  • Paths 提供的 get() 方法用来获取 Path 对象:

    Path get(String first, String … more) : 用于将多个字符串串连成路径。

  1. Files 类
  • java.nio.file.Files 用于操作文件或目录的工具类。

11、自动资源管理

  1. 概念

    Java 7 增加了一个新特性,该特性提供了另外一种管理资源的方式,这种方式能自动关闭文件。这个特性有时被称为自动资源管理(Automatic Resource Management, ARM), 该特性以 try 语句的扩展版为基础。自动资源管理主要用于,当不再需要文件(或其他资源)时,可以防止无意中忘记释放它们。

  2. 自动资源管理基于 try 语句的扩展形式:

    try(需要关闭的资源声明){
    //可能发生异常的语句
    }catch(异常类型 变量名){
    //异常的处理语句
    }
    ……
    finally{
    //一定执行的语句
    } 当

    try 代码块结束时,自动释放资源。因此不需要显示的调用 close() 方法。该形式也称为“ 带资源的 try 语句” 。

    注意:
    ①try 语句中声明的资源被隐式声明为 final ,资源的作用局限于带资源的 try 语句
    ②可以在一条 try 语句中管理多个资源,每个资源以“;” 隔开即可。
    ③需要关闭的资源,必须实现了 AutoCloseable 接口或其自接口 Closeable

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值