NIO学习笔记

NIO比IO强在哪

NIO,即非阻塞的IO,传统IO指的是BIO,使用流来进行文件读写,而NIO使用Channel(通道)和Buffer(缓冲)来进行文件读写。NIO是双全工,非阻塞的。

NIO的"强"主要体现在网络中

1.NIO支持非阻塞IO(ServerSocketChannel 和 SocketChannel),NIO在执行IO操作时不会阻塞,可以继续执行其他的代码;

2.NIO支持多路IO复用(Selector),一个线程可以监听多个通道的请求(接受连接,完成连接,可读,可写)

3.NIO提供了ByteBuffer类,可以高效地管理缓冲区。

ServerSocketChannel和SocketChannel

用于实现非阻塞IO,ServerSocketChannel负责监听连接请求,当设置非阻塞模式后,在等待连接请求时不会阻塞线程,SocketChannel负责发送连接请求和进行数据的读写,当设置成非阻塞模式时,在读写数据时不会阻塞线程。

Selector

用于实现IO多路复用,channel能通过register注册到一个Selector选择器上,这个选择器会不断轮询注册在其上面的所有通道,当通道发生事件时(连接请求,可读,可写),Selector就把通道放到一个就绪集合中,通过get方法可以获取到就绪的Channel并对其进行相应的操作。

注意: 由于 JDK 使用了 epoll() 代替传统的 select 实现,所以它并没有最大连接句柄 1024/2048 的限制。这也就意味着只需要一个线程负责 Selector 的轮询,就可以接入成千上万的客户端。

Selector可以监听的四种类型

1.SelectionKey.OP_ACCEPT:表示接收连接的事件,当ServerSocketChannel收到来自SocketChannel的连接请求时。这个ServerSocketChannel会有OP_ACCEPT事件

2.SelectionKey.OP_CONNECT:表示连接成功事件,当ServerSocketChannel同意来自SocketChannel的连接请求时。这个SocketChannel会有OP_CONNECT事件

3.SelectionKey.OP_READ:表示有数据可读

4.SelectionKey.OP_WRITE:表示可以写入数据

一个Selector实例控制的3个SelectionKey集合

1.所有注册在该Selector实例上的Channel的集合,通过keys()方法获取

2.所有需要进行io操作的Channel的集合,通过selectedKeys()方法获取

3.所有已经删除的Channel的集合

Selector核心方法

select():监控所有注册的Channel,当有需要io操作的Channel出现时,select方法会返回,并且将对应的SelectorKey加入到需要进行io操作的Channel集合中。

Buffer

缓冲区,NIO对数据的操作是利用Channel和基于Buffer进行的,Buffer是一个抽象类,用得比较多的实现类是ByteBuffer,缓冲区的2个重要操作是put和get。因为我们拿到一个缓冲区时,要么是往缓冲区里传输数据,要么是从缓冲区里拿数据。

Buffer的4个变量(limit,position,Capacity,Mark)

然后要说的就是Buffer里面的4个核心变量了,与存取数据操作密切相关。

limit 缓冲区里数据的总数

position 下一个要被读写的位置,调用get和put都会导致position的改变

Capacity容量,是创建缓冲区时的容量,不能被改变,因为缓冲区底层是数组。

Mark 记录上一次读写的位置

Buffer的基础操作

创建缓冲区

ByteBuffer b =ByteBuffer.allocate(1024)

此时四个变量的变化:

limit 1024

position 0

Capacity 1024

Mark java.nio.HeapByteBuffer[pos=0 lim=1024 cap=1024]

往缓冲区添加数据

b.put("我是高手".getBytes);

此时四个变量的变化:

limit 1024

position 12(这里UTF-8一个中文是3个字节,一共12个字节)

Capacity 1024

Mark java.nio.HeapByteBuffer[pos=12 lim=1024 cap=1024]

如果要从缓冲区取出数据,必须先调用flip方法(翻转,为什么叫翻转呢?先往下看)

b.flip();

此时四个变量的变化:

limit 12

position 0

Capacity 1024

Mark java.nio.HeapByteBuffer[pos=0 lim=12 cap=1024]

方法命名为翻转是针对position和limit这2个位置之间的区域来说的,调用flip方法之前是12~1024,调用之后变成了0~12。就像翻转过来了一样。

翻转后就可以取数据了,首先定义一个大小为limit的字节数组(因为只有这么多字节的数据),然后调用get方法将数据存到这个字节数组中。

byte [] bytes =new byte[b.limit()]

b.get(bytes);

此时四个变量又有了变化

limit 12

position 12 这里变成了12

Capacity 1024

Mark java.nio.HeapByteBuffer[pos=12 lim=12 cap=1024]

Channel 通道

buffer负责读写数据,channel负责传输数据?

通道可以分为文件通道和套接字通道

FileChannel,SocketChannel,ServerSocketChannel,DatagramChannel 都属于通道

FileChannel

建立通道:调用FileChannel的open静态方法建立通道,第一个参数是文件路径,第二个参数是访问模式(只读,读写)

复制代码
FileChannel sourceChannel = FileChannel.open(
    Paths.get("logs/javabetter/itwanger.txt"), StandardOpenOption.READ);

使用FileChannel和ByteBuffer将source文件复制到destination文件中思路:

1.首先分别调用open方法创建内存与source文件之间的通道和内存与destination文件之间的通道。

2.调用ByteBuffer的allocate方法创建一个ByteBuffer缓冲区,大小随意,反正复制的时候是一个字节一个字节复制的。

3.开启while循环,调用source文件的通道的read方法,将source文件读取到缓冲区,缓冲区调用flip方法翻转,然后调用destination文件的通道的write方法将缓冲区写入destination文件中去,再调用一次缓冲区的clear方法重置缓冲区的四个变量以及内容。

4.当read方法返回-1时结束

使用MapperByteChannel(内存映射文件)和FileChannel将source文件复制到destination文件中思路:

1.同样的先分别创建2个文件的通道

2.一个MapperByteBuffer对象需要用FileChannel对象的map()方法创建,表示一个文件的映射,创建source和destination的内存映射(即2个MapperByteBuffer对象)。

3.循环读取source的MapperByteBuffer,并写入destination的MapperByteBuffer。

4.调用destination的force方法刷盘,如果不调用,也会刷盘,只不过可能不会立即刷盘。

使用transfer()和FileChannel将source文件复制到destination文件中思路:

1.同样的先分别创建2个文件的通道

2.sourceChannel.transferTo(0, sourceChannel.size(), destinationChannel);调用sourceChannel的transferTo方法。transferTo方法会返回该次操作写的字节数。

底层使用了零拷贝,最大限度的减少了数据传输过程中的CPU和内存开销

直接缓冲区和非直接缓冲区

1.非直接缓冲区

创建方式:ByteBuffer.allocate()

位置:非直接缓冲区存在于JVM的堆空间中,收到GC的管理

使用非直接缓冲区进行io操作时,需要将数据从JVM堆中复制到本地内存中,再进行io操作

2.直接缓冲区

创建方式:ByteBuffer.allocateDirect()

位置:直接缓冲区分配在本地内存中,不受GC的管理

在使用直接缓冲区进行IO操作时,直接在本地内存中进行。

异步文件通道 AsynchronousFileChannel

相关推荐
菜的不敢吱声7 小时前
swift学习第4天
服务器·学习·swift
晚枫歌F10 小时前
Dpdk介绍
linux·服务器
李慕婉学姐10 小时前
【开题答辩过程】以《基于JAVA的校园即时配送系统的设计与实现》为例,不知道这个选题怎么做的,不知道这个选题怎么开题答辩的可以进来看看
java·开发语言·数据库
web小白成长日记10 小时前
企业级 Vue3 + Element Plus 主题定制架构:从“能用”到“好用”的进阶之路
前端·架构
APIshop11 小时前
Python 爬虫获取 item_get_web —— 淘宝商品 SKU、详情图、券后价全流程解析
前端·爬虫·python
风送雨11 小时前
FastMCP 2.0 服务端开发教学文档(下)
服务器·前端·网络·人工智能·python·ai
XTTX11011 小时前
Vue3+Cesium教程(36)--动态设置降雨效果
前端·javascript·vue.js
LYFlied12 小时前
WebGPU与浏览器边缘智能:开启去中心化AI新纪元
前端·人工智能·大模型·去中心化·区块链
奋进的芋圆12 小时前
Java 延时任务实现方案详解(适用于 Spring Boot 3)
java·spring boot·redis·rabbitmq
Setsuna_F_Seiei12 小时前
2025 年度总结:人生重要阶段的一年
前端·程序员·年终总结