一、NIO概述
NIO(Non-blocking I/O,非阻塞I/O)是Java中的一个高性能I/O框架,它提供了一种简单、高效的I/O处理方式,NIO的核心思想是将I/O操作从同步变为异步,从而提高程序的并发性能,NIO主要包括Buffer、Channel和Selector三个核心组件,通过这三个组件可以实现非阻塞的文件读写、网络通信等功能。
二、NIO的基本概念
1. Buffer
Buffer是NIO中用于存储数据的容器,它可以在内存中直接进行读写操作,提高了I/O操作的效率,NIO中的Buffer分为两类:ByteBuffer和CharBuffer,ByteBuffer主要用于处理字节数据,而CharBuffer主要用于处理字符数据。
2. Channel
Channel是NIO中用于处理I/O操作的通道,每个Channel对应一个文件或网络连接,通过Channel可以进行数据的读取、写入等操作,NIO中的Channel分为两类:FileChannel和SocketChannel,FileChannel用于处理文件I/O操作,而SocketChannel用于处理网络I/O操作。
3. Selector
Selector是NIO中的选择器,它负责管理多个Channel的状态,当某个Channel上有事件发生时(如读、写事件),Selector会通知关联的线程进行处理,这样可以实现多个线程同时处理多个Channel的I/O操作,提高程序的并发性能。
三、NIO的工作流程
1. 创建FileChannel或SocketChannel对象,并与指定的文件或网络连接建立关联关系。
2. 创建一个Selector对象,并将需要处理的Channel注册到Selector上。
3. 向Selector注册感兴趣的事件类型(如读、写事件)。
4. 调用Selector的select方法,等待事件发生,当事件发生时,Selector会通知关联的线程进行处理。
5. 在处理事件的线程中,通过Selector获取关联的Channel,并进行相应的I/O操作。
6. 关闭Selector和相关资源。
四、NIO的示例代码
下面是一个简单的NIO示例代码,实现了一个简单的文件复制功能:
```java
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;
public class NioDemo {
public static void main(String[] args) throws IOException {
// 创建文件输入输出流
FileInputStream fis = new FileInputStream("source.txt");
FileOutputStream fos = new FileOutputStream("destination.txt");
// 创建文件通道
FileChannel inChannel = fis.getChannel();
FileChannel outChannel = fos.getChannel();
// 创建选择器
Selector selector = Selector.open();
// 向选择器注册读事件
inChannel.register(selector, SelectionKey.OP_READ);
// 创建服务器套接字通道,并绑定端口监听连接请求
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.bind(new InetSocketAddress(8080));
// 向选择器注册服务就绪事件,并将服务器套接字通道注册到选择器上
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
// 进入事件循环,等待事件发生并处理
while (true) {
int readyChannels = selector.select(); // 等待事件发生,超时时间为0表示立即返回,不等待;其他正数表示等待的时间(单位:毫秒)
if (readyChannels == 0) { // 如果超时时间到了,继续等待下一个事件循环;否则跳出循环,处理已发生的事件
continue;
} else { // 处理已发生的事件
Set selectedKeys = selector.selectedKeys(); // 获取已选中的键集合(包括关注的事件类型和对应的通道)
Iterator keyIterator = selectedKeys.iterator(); // 遍历键集合(注意要使用迭代器而不是普通for循环)
while (keyIterator.hasNext()) { // 遍历已选中的键集合(即已发生的事件)
SelectionKey key = keyIterator.next(); // 获取当前键(即当前已发生的事件)
if (key != null) { // 如果当前键不为空(即存在已发生的事件)
if (key.isAcceptable()) { // 如果当前键是服务就绪事件(即有新的客户端连接请求)
ServerSocketChannel server = (ServerSocketChannel) key.channel(); // 获取服务端通道(即新建立的客户端连接)
SocketChannel client = serverSocketChannel.accept(); // 接受客户端连接请求,返回一个新的客户端通道(即与客户端建立连接的套接字)
client.configureBlocking(false); // 将客户端通道设置为非阻塞模式(因为NIO是基于事件驱动的,不需要使用同步I/O)
client.register(selector, SelectionKey.OP_READ); // 向选择器注册读事件(即准备接收客户端发送的数据)
client.write(ByteBuffer.wrap("Hello, World!".getBytes())); // 向客户端发送数据(这里只是简单地发送了一行文本)
} else if (key.isReadable()) { // 如果当前键是读事件(即有数据可读)
// 根据当前的SelectionKey获取关联的通道(即读事件的源通道)和服务端的套接字通道(即客户端连接的源套接字)
SocketChannel client = (SocketChannel) key.channel(); // 获取客户端通道(即与客户端建立连接的套接字)和服务器套接字通道(即服务端监听客户端连接请求的套接字)
ByteBuffer buffer = ByteBuffer.allocate(1024); // 根据需要分配缓冲区大小(这里为1KB)的空间来存储数据(这里只是简单地读取了一行文本)
int bytesRead = client.read(buffer); // 从客户端通道中读取数据到缓冲区中(这里只是简单地读取了一行文本)
if (bytesRead > 0) { // 如果读取到了数据(即缓冲区中有数据)则准备将其写入到目标通道中(这里只是简单地将数据写入到了文件通道中)
buffer.flip(); // 切换缓冲区为读模式(即将写模式切换为读模式,以便写入数据到目标通道中)
原创文章,作者:K-seo,如若转载,请注明出处:https://www.kdun.cn/ask/28226.html