BIO、NIO、AIO
1、Java NIO
始于Java1.4,提供了新的Java IO操作非阻塞API。用来替代Java IO和Java Networking相关的API。
NIO核心组件:Buffer缓冲区、Channel通道、Selector选择器
2、Buffer缓冲区
本质上是一个可以写入/读取数据的内存块(类似数组),此内存块包含在NIO Buffer对象中,该对象提供了一组方法,可以更加轻松的使用内存块。相比直接操作数组,Buffer API更加容易操作和管理。
- 使用Buffer进行数据写入与读取的四个步骤:
a.将数据写入缓冲区
b.调用buffer.flip(),转换为读取模式
c.缓冲区读取数据
d.调用buffer.clear()或buffer.compact()清除缓冲区
- Buffer工作原理:三个重要属性
capacity容量:Buffer具有一定的固定大小,也称为容量
position位置:写入/读取模式时分别代表写数据/读取数据的位置
limit限制:写入模式限制等于Buffer的容量,读取模式limit等于写入的数据量
- Buffer常用方法:
put() 写入数据
flip() 转换为读取模式
get() 读取数据
rewind() 重置position为0
mark() 标记position的位置
reset() 重置position为上次mark()标记的位置
clear() 清除整个缓冲区
compact() 仅清除已阅读的数据
- ByteBuffer(字节缓冲区)内存类型
ByteBuffer为性能关键型代码提供了直接内存(direct堆外)和非直接内存(heap堆)两种实现。
堆外内存获取方式:ByteBuffer.allocateDirect(noBytes);
构建容量为bytes的字节缓冲区:ByteBuffer.allocate(bytes);将申请堆内存,在堆内存申请的缓冲区是数组的形式
- 直接(堆外)内存的好处与建议:
3、Channel通道
Channel的API涵盖了UDP/TCP网络和文件IO:
FileChannel、SocketChannel、ServerSocketChannel、DatagramChannel
和标准IO Stream操作的区别:
NIO:在一个通道内进行读取和写入,可以非阻塞读取和写入通道,通道始终读取或写入数据到缓冲区;通道可以创建网络连接,也可以传输数据
BIO:而Stream通常是单向的(input或output),InputStream.read()读取数据阻塞;通过Socket创建连接,Stream传输数据(InputStream、OutputStream)
SocketChannel:用于建立TCP网络连接,类似Java.net.Socket。
ServerSocketChannel:作用于服务端,可以绑定端口,监听新建的TCP连接通道,类似ServerSocket。可以设置为非阻塞模式,ServerSocketChannel.accept()非阻塞时如果没有监听到新的连接会立即返回null。
问题:由于使用非阻塞读取写入数据,需要循环检查数据是否真正完成读取和写入,才能进行后续操作。为了避免这一情况,NIO提供了Selector组件。
4、Selector选择器
可以检查一个或多个NIO通道,确定哪些通道已准备好进行数据读取或写入。实现单个线程可管理多个通道,从而管理多个网络连接。
一个线程使用Selector监听多个Channel的不同事件,四个事件分别对应SelectionKey四个常量:
OP_CONNECT:Connect连接
OP_ACCEPT:Accept准备就绪
OP_READ:Read读取
OP_WRITE:Write写入
核心概念:事件驱动机制。非阻塞的网络通道下,开发者通过Selector注册对于通道感兴趣的事件类型,线程通过监听事件来触发相应的代码执行。更底层是操作系统的多路复用机制。
多路复用:面试之多路复用_coolgw2015的博客-CSDN博客_多路复用
Selector.select()有阻塞效果,有事件通知才返回
BIO:阻塞IO,线程等待时间长、一个线程负责一个连接处理、线程多且利用率低
NIO:非阻塞IO,线程利用率高、一个线程处理多个连接事件、性能更强大
如果程序需要支撑大量的连接,则使用NIO。Tomcat8默认采用NIO进行网络处理。
问题:一个Selector监听所有事件,一个线程处理所有请求连接,会成为瓶颈。需要NIO与多线程结合运用。
对Java NIO进行封装、增强后的网络编程框架:Netty、Mina等。