文章目录
NIO 的引入
在传统的 Java I/O 模型(BIO)中,I/O 操作是以阻塞的方式进行的。当一个线程执行一个 I/O 操作时,它会被阻塞直到操作完成。这种阻塞模型在处理多个并发连接时可能会导致性能瓶颈,因为需要为每个连接创建一个线程,而线程的创建和切换都是有开销的。
为了解决这个问题,在 Java1.4 版本引入了 NIO
(New I/O or Non-Blocking I/O)java.nio
。提供了一种基于缓冲区、选择器和非阻塞 IO 模型的 IO 处理方式。相比于之前的 BIO
模型,NIO
可以实现更高的并发、更低的延迟以及更少的资源消耗。
I/O 包和 NIO
已经很好地集成了,java.io
也已经以 NIO
为基础重新实现了,所以现在它可以利用 NIO
的一些特性。例如,java.io
包中的一些类包含以块的形式读写数据的方法,这使得即使在面向流的系统中,处理速度也会更快。
使用 NIO 并不一定意味着高性能,它的性能优势主要体现在高并发和高延迟的网络环境下。当连接数较少、并发程度较低或者网络传输速度较快时,NIO 的性能并不一定优于传统的 BIO 。
Buffer
在 NIO(New Input/Output)模型中,Buffer 是一个重要的概念,与数据打交道,用于在内存中存储数据。通过 Channel 将数据传输到 Buffer 缓冲区中,并在缓冲区内进行数据的读写操作。
Buffer
本质上是一个数组,可以存储多个相同类型的基本数据类型,如 byte、short、int、long、float、double 等。Buffer 封装了内部的数组,并提供了一些操作该数组的方法。
NIO 提供了多种 Buffer 类型,如
ByteBuffer
(最常用的 Buffer 类)CharBuffer
ShortBuffer
IntBuffer
LongBuffer
FloatBuffer
DoubleBuffer
等
每种 Buffer
类型对应着不同的基本数据类型,用于存储不同类型的数据。
核心变量
Buffer
除了存储数据外,还保存着四个关键属性:
-
capacity
:表示 Buffer 的容量,即可以存储的最大数据量,容量在创建 Buffer 时就确定,之后不可修改。 -
position
:表示当前 Buffer 中已经处理的数据位置,初始值为 0,必须手动设置。每当进行读写操作时,Position 会自动由相应的get()
和put()
函数更新,向前移动 -
limit
:表示 Buffer 中可供访问的数据的最大位置(上界),初始值为 capacity,可以手动设置为 position 或其他值。 -
mark
:表示一个备忘记录,用于在某个特定位置设置 mark,然后在之后的某个时间点通过调用reset()
方法恢复到该 position。
在进行数据读写操作时,一般需要调用 put()
方法将数据写入 Buffer 中,或者调用 get()
方法从 Buffer 中读取数据。
对于写入操作,由于 position 属性的存在,保证了写入的数据不会覆盖已有的数据;对于读取操作,由于 position 属性的存在,保证了读取的数据不会超出可访问的数据范围。
使用 Buffer 进行数据读写时,需要注意处理好 position 和 limit 属性的值,以及不同类型的 Buffer 之间的数据转换问题。Buffer 并不是线程安全的,使用时需要注意线程同步的问题。
常用方法
-
put()
:向 Buffer 中写入数据。put() 方法有多个重载形式,它们可以将不同类型的数据写入 Buffer 中。例如 put(byte b)、putInt(int i)、putFloat(float f) 等。-
put(type value)
:将指定类型的数据写入到 Buffer 中,position 会自动向前移动。 -
put(byte[] array)
:将 byte 数组从当前 position 处写入 Buffer,同时会增加 position 的值。 -
put(ByteBuffer src)
:将 src 中的剩余字节写入 Buffer,同时会增加 position 的值。
-
-
get()
:从 Buffer 中读取数据。get() 方法也有多个重载形式,根据不同的数据类型可以选择对应的 get() 方法进行读取。例如,getInt()、getFloat()、getChar() 等。-
get()
:从当前 position 处读取一个字节,并将 position 向前移动。 -
get(byte[] array)
:将从当前 position 处开始的字节序列读入给定的 byte 数组中,并增加 position 的值。 -
get(ByteBuffer dst)
:将从当前 position 处开始的字节序列读入给定的 ByteBuffer 中,并增加 position 和 dst 的 position 值。
-
-
flip()
:设置 Buffer 的 limit 属性 为 当前的 position,然后将 position 属性设置为 0。在写入数据后,调用 flip() 方法可以将 Buffer 切换到读模式。 -
rewind()
:将 position 属性设置为 0,不改变 limit 属性,可以重复读取 Buffer 中的数据。 -
clear()
:将 Buffer 清空,position 和 limit 属性设置为初始值。可以重复写入 Buffer