本次文章主要给大家带来NIO的相关知识点,一些基础的理论知识,巩固大家的对NIO的理解。好了废话不多说直接步入正题。
什么是NIO?
顾名思义NIO:
non-blocking io
也可称作new io
非阻塞 IO
Channel
&Buffer
Channel:
双向通道
,可以将数据读入buffer,也可以将buffer的数据写入Channel,而之前的stream要么是输入要么就是输出,由此可见Channel比stream更为底层创建的Channel:
Buffer
常见的Buffer
⚠️ 多线程版缺点
内存占用较高 线程上下文切换的成本高 只适合连接数少的场景
⚠️ 线程池版缺点
阻塞模式下,线程仅能处理一个socket连接 仅适合短链接场景
selector版
的设计
selector基本概念selector的作用就是配合一个线程
来管理多个channel
,获取这些channel上发生的事件,这些channel工作在非阻塞模式下,不会让线程吊死在一个channel上。适合连接数特别多
,但流量低
的场景(low traffic
)
大致如图所示:读写就绪事件
Channel获取方式(FileChannel的获取)
ByteBuffer获取方式
通过静态方法ByteBuffer.allocate(16)
@Slf4j
public class TestByteBuffer {
public static void main(String[] args) {
// FileChannel
// 1. 输入输出流,
// 2. RandomAccessFile
// RandomAccessFile file = new RandomAccessFile("helloword/data.txt", "rw")
try (FileChannel channel = new FileInputStream("data.txt").getChannel()) {
// 准备缓冲区
ByteBuffer buffer = ByteBuffer.allocate(10);
while(true) {
// 从 channel 读取数据,向 buffer 写入
int len = channel.read(buffer);
log.debug("读取到的字节数 {}", len);
if(len == -1) { // 没有内容了
break;
}
// 打印 buffer 的内容
buffer.flip(); // 切换至读模式
while(buffer.hasRemaining()) { // 是否还有剩余未读数据
byte b = buffer.get();
log.debug("实际字节 {}", (char) b);
}
buffer.clear(); // 切换为写模式
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
data.txt的内容是:
1234567890abcd
flip()
clear()
或compact()
切换至写模式,后面介绍两种方法的不同之处。ByteBuffer有以下的重要属性:
ByteBuffer初始化的时候:
写模式下,position
是写入位置,limit等于容量,下图是写入了4个字节后的状态。
当发生filp()
动作后,position
切换为读取位置
,limit切换为读取限制。
读取了4个字节后
clear动作发生后,ByteBuffer进行了初始化
:
compact()方法,是把未读完的部分向前压缩
,然后切换到写模式下,如图所示: (这里将clear和compact进行了讲解,大致区别也是如此)
例如:
ByteBuffer buf = ByteBuffer.allocate(10);
channel
的read
方法;例如:
int readBytes = channel.read(buffer);
buf.put((byte)127);
channel
的write
方法;例如:
int writeBytes = channel.write(buf);
byte b = buf.get()
💡 注意:
buffer的
get()
方法会让position读指针向后移动,如果想要重复读取数据:
mark是在读取的时候,做一个
标记
,即使position发生改变,只要调用reset
方法就能回到mark的位置
💡 注意:
rewind
和flip
都会清楚掉mark位置,前者是将position重置为0,后者是切换读模式,切记切记
/**
* 1.字符串转化为ByteBuffer
* 此方法要想将ByteBuffer转换为字符串则需要切换为读模式-使用flip()方法 该方法还是写模式
*/
ByteBuffer buffer1 = ByteBuffer.allocate(16);
buffer1.put("hello".getBytes());
debugAll(buffer1);
/**
* 2.字符串转化为ByteBuffer
*/
ByteBuffer buffer2 = StandardCharsets.UTF_8.encode("hello");
debugAll(buffer2);
/**
* 3.字符串转化为ByteBuffer
*/
ByteBuffer buffer3 = ByteBuffer.wrap("hello".getBytes());
debugAll(buffer3);
/**
* ByteBuffer 转换为字符串
*/
String str3 = StandardCharsets.UTF_8.decode(buffer3).toString();
System.out.println(str3);