Java NIO介绍及说明
2023-05-04 19:58:23 0 举报
AI智能生成
Java NIO 主要包含以下几个部分: 1. 缓冲区(Buffer):缓冲区是一个在内存中预留一块区域,用于存放读取或写入数据的区域。Java NIO 中的缓冲区类似于数组,可以读取和写入数据,同时支持更高效的数据操作。 2. 通道(Channel):通道是 Java NIO 中的另一个重要概念,用于对缓冲区数据的读取和写入。与传统的 Java I/O 中的流(Stream)不同,通道可以同时进行读和写操作,并且可以从缓冲区读取数据,也可以将数据写入到缓冲区中。 3. 选择器(Selector):选择器是 Java NIO 中的另一个重要概念,用于监控多个通道的事件状态(如连接请求、数据到达等)。选择器可以帮助我们避免使用线程池等多线程机制,从而实现更高效的 I/O 操作。 Java NIO 还提供了众多的用于处理网络和文件 I/O 的类和接口,如 SocketChannel、ServerSocketChannel、DatagramChannel、FileChannel、CharsetDecoder 等,这些类和接口的组合能够实现高效的 I/O 操作。
作者其他创作
大纲/内容
vs 传统IO
传统IO面向流(字节流和字符流)
每次从流中读取一个或多个字节
没有被缓存在任何地方
不能前后移动流中的数据
NIO 面向缓冲区(缓冲导向)
数据读取到一个稍后处理的缓冲区
需要时可在缓冲区中前后移动
需要检查缓冲区中是否包含需要处理的数据
需确保当更多数据读入缓冲区时,不覆盖缓冲区里未处理的数据
传统IO的各种流是阻塞的
当一个线程 read() 或 write() 时,该线程阻塞直到一些数据被读取或数据完全写入
在此期间不能再干别的事情(被阻塞)
NIO是非阻塞的
非阻塞读
仅能得到目前可用的数据
如果目前没有数据可用,就什么都不会读取
在数据变得可读取之前,线程可以做其他工作
非阻塞写
不需要等待数据完全写入
写入到缓冲区
通常将非阻塞IO的空闲时间用于在其他通道上执行IO操作
一个线程可以管理多个输入输出通道
三大核心部分
Channel(通道)
描述
Channel 是双向的,可写可读
主要实现
FileChannel
DatagramChannel
SocketChannel
ServerSocketChannel
使用(以SocketChannel为例)
配置为非阻塞
channel.configureBlocking(false)
在非阻塞的信道上调用一个方法总是会立即返回
返回值表示所请求的操作的完成程度
在一个非阻塞式的 ServerSocketChannel 上调用 accept() 方法
如果有连接请求,则返回客户端 SocketChannel
如果没有请求,返回 null
accept() 方法
非阻塞模式下立刻返回
null
有连接则返回 SocketChannel
write() 方法放在 while 循环中
条件 buf.hasRemaining()
因为write()方法无法保证能写多少字节到SocketChannel,所有重复调用write()知道Buffer中没有要写的字节位置
非阻塞模式下,read()可能在尚未读取到任何数据时就返回了,所以需要关注它的int返回值,表示读取了多少字节
Buffer(缓冲区)
描述
可以把 Buffer 简单的理解成一组基本数据类型的数组
通过几个变量来保存这个数据的当前位置状态
capacity
缓冲区数组的总长度
position
下一个要操作的数据元素的位置
limit
缓冲区数组中不可操作的下一个元素的位置
limit <= capacity
mark
记录当前 position 的前一个位置
默认是 -1
向 Buffer 中写数据
从 Channel 写到 Buffer
fileChannel.read(buf)
通过 Buffer 的 put() 方法
buf.put()
从 Buffer 中读数据
从 Buffer 读取到 Channel
channel.write(buf)
通过 Buffer 的 get() 方法
buf.get()
主要实现
MappedByteBuffer
HeapByteBuffer
DirectByteBuffer
七种数据类型 Buffer(ByteBuffer等)
使用
1. 分配空间
ByteBuffer buf = ByteBuffer.allocate(1024)
2. 写入数据到 buffer
int bytesRead = fileChannel.read(buf)
3. 调用 flip() 方法
position回0
limit 设置为之前 position 的值
4. 从 Buffer 中读取数据
buf.get()
5. 调用 clear() 或 compact()
clear()
position回0
limit = capacity
清空缓冲区,未读数据将被遗忘
compact()
将未读数据拷贝到 Buffer 起始处
position = 未读数据的位置 + 1
limit = capacity
Buffer 可以重新写入数据,但不会覆盖未读数据
mark() 方法
可以标记 Buffer 中的一个特定的 position
之后可以通过调用 reset() 方法恢复到这个 position
rewind() 方法
将position回0
重读 Buffer 中的所有数据
limit 保持不变
表示能从 Buffer 中读取多少个元素
Selector(选择器)
描述
考虑一个 IM 服务器
可能有几千万客户端同时连接到了服务器
但是在任何时刻都只有非常少量的消息
这就需要一种方法阻塞等待
直到至少一个信道可以进行 IO 操作,并指出是哪个信道
选择器就是一个多路开关选择器
因为一个选择器能够管理多个信道上的 IO 操作
返回结果有两种
0
在调用的时刻没有任何客户端需要 IO 操作
一组需要 IO 操作的客户端
使用
1. 创建 Selector 实例
使用静态工厂方法 open()
Selector.open()
2. 将想要监控的信道(channel)注册(register)到 Selector 上
channel.register(selector, SelectionKey.OP_ACCEPT);
Channel 必须处于非阻塞模式
FileChannel 不能和 Selector 一起使用
因为它不能切换到非阻塞模式
套接字 Channel 都可以
accept中的第二个参数是 Selector 的兴趣点
表示 Selector 对监听的 Channel 的什么事件感兴趣
Connect
Accept
Read
Write
3. 调用 select() 方法
返回可进行 IO 操作的信道数量
如果经过一段时间后,仍然没有信道准备好,返回0
int select(long timeout) 可以设置超时时间
selectNow() 不会阻塞
没有就返回0
select() 阻塞等待
该方法会阻塞等待,直到有一个或更多的信道准备好了 IO 操作或等待超时
4. 调用 selectKeys() 方法
就绪的 SelectionKey 的集合
迭代完成后调用 key 的 remove() 方法,将其从迭代器移除
必须手动移除
下次就绪时,Selector 会再次将其放入已选择键集中
SelectionKey
register() 方法返回
包含
interest 集合
Selector 的兴趣点
可以通过 SelectionKey 读写 interest 集合
ready 集合
通道已经准备 IO 操作的集合
Channel
selectionKey.channel()
需要转型成需要处理的类型
如 ServerSocketChannel 或 SocketChannel 等
Selector
selectionKey.selector()
附加的对象(可选)
selectionKey.attach(Object)
内存映射文件
说明
处理超大文件,采用 MappedByteBuffer
MappedByteBuffer 是 NIO 引入的文件内存映射方案
读写性能极高
将文件直接映射到内存(虚拟内存)
实现了对异步操作的支持
FileChannel
提供了 map() 方法来把文件映射为内存映像文件
MappedByteBuffer map(int mode, long position, long size)
可以把文件的从 position 开始的 size 大小的区域映射为内存映像文件
mode 指出可访问该内存映像文件的方式
READ_ONLY
只读
尝试写入抛出 ReadOnlyBufferException
READ_WRITE
对缓冲区的更改最终将传播到文件
该更改对映射到同一文件的其他程序不一定是可见的
PRIVATE
对缓冲区的更改不传播到文件
其他程序不可见
创建缓冲区已修改部分的专用副本
MappedByteBuffer
是 ByteBuffer 的子类
扩充了三个方法
force()
在 READ_WRITE 模式下,对缓冲区内容的修改强行写入文件
load()
将缓冲区的内容载入内存,并返回该缓冲区的引用
isLoaded()
如果在物理内存中,返回 true
有资源释放的问题
被 MappedByteBuffer 打开的文件只是在GC时才会被关闭
0 条评论
下一页