NIO学习笔记

1 简介

Java NIO是从java 1.4版本开始引入的一个新的IO API

  • 目的—替代标准版的Java IO API

NIO与原来的IO有同样的作用和目的,但是使用方式完全不同

  • NIO支持面向缓冲区的,基于通道的操作
  • NIO以更高效的方式进行文件的读写操作

2 与IO的主要区别

IO

IONIO
面向流(Stream Oriented)面向缓冲区(Buffer Oriented)
阻塞IO(Blocking IO)非阻塞IO(Non Blocking IO)
选择器(Selectors)

3 通道和缓冲区

NIO系统的核心在于:

  • 通道(Channel)—打开到IO设置(例如:文件, 套接字)连接
  • 缓冲区(Buffer)—一个用于特定基本数据类型的容器
    由java.nio包定义,所有缓冲区都是Buffer抽象类的子类

若需要使用NIO系统

  • 需要获取用于连接IO设备的通道以及用于容纳数据的缓冲区.
  • 然后操作缓冲区,对数据进行处理

简而言之, Channel负责传输,Buffer负责存储

3.1 缓冲区

缓冲区(Buffer):在Java NIO中负责数据的存取. 缓冲区就是数组,用于存储不同数据类型的数据

根据数据类型不同(Boolean除外),提供了相应的缓冲区

  • ByteBuffer
  • CharBuffer
  • ShortBuffer
  • IntBuffer
  • LongBuffer
  • FloatBuffer
  • DoubleBuffer

上述缓冲区的管理方式几乎一致,通过allocate获取缓冲区

缓冲区存取数据的两个核心方法

  • put()—存入数据到缓冲区中
  • get()—获取缓冲区中的数据

缓冲区中的四个核心属性

  • capacity—容量,标识缓冲区最大存储数据的容量,一旦声明就不能改变
  • limit—界限,表示缓冲区中可以操作数据的大小limit后面的数据不能读写
  • position—位置,表示缓冲区中正在操作数据的位置
    position <= limit <= capacity
  • mark—标记, 表示记录当前position的位置,可以通过reset()恢复到mark位置
    0 <= mark <= position <= capacity

3.2 直接缓冲区与非直接缓冲区

  • 非直接缓冲区—通过allocate()方法分配缓冲区,将缓冲区建立在JVM的内存中
  • 直接缓冲区—通过allocateDirect()方法分配直接缓冲区,将缓冲区建立在物理内存中,可以提高效率

字节缓冲区要么是直接的,要么是非直接的. 如果为直接字节缓冲区,则JVM会尽最大努力直接在此缓冲区上执行本机的I/O操作

也就是说,在每次调用基础操作系统的一个本机I/O操作之前(或之后),虚拟机都会尽量避免将缓冲区中的内容复制到中间缓冲区中(或从中间缓冲区中复制内容)

直接字节缓冲区可以通过调用此类的allocateDirect()方法来创建. 此方法返回的缓冲区进行分配和取消分配所需成本通常高于非直接缓冲区. 直接缓冲区的内容可以在主流在常规的垃圾回收器之外. 因此, 他们对应用程序的内容需求量造成的影响可能并不明显. 所以, 建议将直接缓冲区主要分配给哪些容易受基础系统的本机I/O操作影响的大型, 持久的缓冲区.

一般情况下,最好仅在直接缓冲区能在程序性能方面带来明显好处时分配它们

直接缓冲区还可以通过FileChannel的map()方法将文件区域直接映射到内存中来创建. 该方法返回MappedByteBuffer.

Java平台的实现有助于通过JNI从本机代码创建直接字节缓冲区.

如果以上这些缓冲区中某个缓冲区实例指的是不可用访问的内存区域,则试图访问该区域不会更改该缓冲区的内容.并且将会在访问期间或稍后的某个时间导致抛出不确定的异常

字节缓冲区是直接缓冲区还是非直接缓冲区可以通过其isDirect()方法来确定.提供此方法是为了能够在性能关键代码中执行显示缓冲区管理

如下图所示
非直接缓冲区

直接缓冲区

3.3 通道

通道(Channel)—由java.nio.channels包定义的
Channel表示IO源与目标打开的连接

用于源节点和目标节点的连接, 在Java NIO中负责缓冲区中的数据传输. Channel本身不存储数据,因此需要配合缓冲区进行传输

Channel类似于传统的"流", 只不过Channel本身不能直接访问数据,Channel只能与Buffer进行交互

CPU直接处理I/O请求
CPU直接处理I/O请求
通过DMA—直接存储器处理IO请求
通过DMA---直接存储器处理IO请求
通过通道处理IO请求
通过通道处理IO请求

3.3.1 主要实现类

java.nio.channels.Channel接口

  • FileChannel—文件传输
  • SocketChannel—网络传输(客户端)
  • ServerSocketChannel—网络传输(服务端)
    -DatagramChannel—网络传输, UDP

获取通道

  1. Java针对支持通道的类提供了getChannel()方法
  • 本地IO
    • FileInputStream/FileOutputStream
    • RandomAccessFile
  • 网络IO
    • Socket
    • ServerSocket
    • DatagramSocket
  1. 在JDK 1.7中的NIO针对各个通道提供了静态方法open()
  2. 在JDK 1.7中的Files工具类的newByteChannel()
  3. 通道之间的数据传输
    • transferFrom
    • transferTo
      两者也是通过直接缓冲区传递数据

3.4 分散(Scatter)和聚集(Gather)

  • 分散读取Scatter Reads是指从Channel中读取的数据分散到各个Buffer中

注意: 按照缓冲区的顺序,从Channel中读取的数据依次将Buffer填满

  • 聚集写入Gather Writes是指将多个Buffer中的数据"聚集"到Channel

注意: 按照缓冲区的顺序,写入position和limit之间的数据到Channel

3.5.字符集

Charset

  • 编码—字符串 —> 字节数组
  • 解码—字节数组 —>字符串

4 NIO非阻塞式

4.1 阻塞与非阻塞

传统的的IO流都是阻塞的. 也就是说,当一个线程调用read()或write()时,该线程被阻塞, 直到有一些数据被读取或写入, 该线程在此期间不能执行其他任务.

因此, 在完成网络通信进行IO操作时, 由于线程会阻塞, 所以服务器补习为每个客户端都提供一个独立的线程进行处理, 当服务端需要处理大量客户端时, 性能急剧下降


Java NIO是非阻塞式的. 当线程从某通道进行读写数据时, 若没有数据可用时, 该线程可以进行其他任务, 线程通常将非阻塞IO的空闲时间用于在其他通道上执行IO操作, 所以单独的线程可以管理多个输入和输出通道

因此, NIO可以让服务端使用一个或有限几个线程来同时处理连接到服务器端的所有客户端

4.2 NIO

使用NIO完成网络通信的三个核心

  • 通道(Channel)—负责连接
  • 缓冲区(Buffer)—负责数据的存储
  • 选择器(Selector)—是SelectableChannel的多路复用器
    用于监控SelectableChannel的IO状况

4.3 选择器

选择器(Selector)是SelectableChannel对象的多路复用器

Selector可以同时监控多个SelectableChannel的状态, 也就是说, 利用Selector可以使一个单独的线程管理多个Channel. Selector是非阻塞IO的核心

SelectableChannel的结构如下:

  • SelectableChannel
    • AbstractSelectableChannel
      • SocketChannel
      • ServerSocketChannel
      • DatagramChannel

4.3.1 SelectionKey

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

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

    • 读—SelectionKey.OP_READ
    • 写—SelectionKey.OP_WRITE
    • 连接—SelectionKey.OP_CONNECT
    • 接收—SelectionKey.OP_ACCEPT

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java NIO(New I/O)是一个可以替代标准 Java I/O API 的 Java API。NIO 提供了更快速、更高效的 I/O 操作方式,也可以进行非阻塞 I/O 操作。 下面是几个 NIO 核心类和接口: - Buffer:缓冲区,用于数据的读取和写入。 - Channel:通道,用于数据的读取和写入。 - Selector:选择器,用于监控多个通道的 I/O 状态,从而实现多路复用。 下面介绍一下 NIO 的主要操作步骤: 1. 创建 Buffer 对象:创建一个 Buffer 对象,用于存储数据。 2. 创建 Channel 对象:创建一个 Channel 对象,用于读取和写入数据。 3. 打开 Channel:打开一个 Channel 对象,并连接到指定的服务器。 4. 将数据写入 Buffer:将需要写入的数据写入到 Buffer 中。 5. 切换 Buffer 模式:将 Buffer 从写模式切换到读模式。 6. 从 Buffer 中读取数据:从 Buffer 中读取数据。 7. 关闭 Channel:关闭 Channel 对象。 8. 关闭 Selector:关闭 Selector 对象。 下面是一个简单的 NIO 例子: ```java import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.SocketChannel; public class NIOClient { public static void main(String[] args) throws IOException { SocketChannel socketChannel = SocketChannel.open(); socketChannel.connect(new InetSocketAddress("localhost", 8888)); ByteBuffer buffer = ByteBuffer.allocate(1024); buffer.put("Hello, server!".getBytes()); buffer.flip(); socketChannel.write(buffer); buffer.clear(); int num = socketChannel.read(buffer); System.out.println("Received from server: " + new String(buffer.array(), 0, num)); socketChannel.close(); } } ``` 这个例子中,我们先创建了一个 SocketChannel 对象,并连接到指定的服务器。然后,我们创建了一个 ByteBuffer 对象,将需要写入的数据写入到 ByteBuffer 中,切换 ByteBuffer 的模式,从中读取数据并输出到控制台,最后关闭 SocketChannel 对象。 希望这份简单的介绍能够帮助你更好的了解 Java NIO API。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值