NIO的实战教程(简单且高效)

1. 参考

建议按顺序阅读以下三篇文章
为什么NIO被称为同步非阻塞?
Java IO 与 NIO:高效的输入输出操作探究
【Java.NIO】Selector,及SelectionKey

2. 实战

我们将模拟一个简单的HTTP服务器,它将响应客户端请求并返回一个固定的响应("Hello, World!")。我们将使用IO和NIO两种不同的方式实现此服务器。
2.1 传统阻塞IO

java 复制代码
import java.io.*;
public class TraditionalIOExample {
    public static void main(String[] args) {
        try {
            // 打开文件
            InputStream input = new FileInputStream("example.txt");
            OutputStream output = new FileOutputStream("output.txt");

            // 读取和写入数据
            int data;
            while ((data = input.read()) != -1) {
                output.write(data);
            }

            // 关闭文件
            input.close();
            output.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

2.2 非阻塞NIO

java 复制代码
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.Set;

public class NioHttpServer {
    public static void main(String[] args) {
        try {
        	// 创建服务端通道
            ServerSocketChannel serverChannel = ServerSocketChannel.open();
            // 绑定访问端口
            serverChannel.socket().bind(new InetSocketAddress(8080));
            // 通道设置为非阻塞
            serverChannel.configureBlocking(false);

			// 通过open方法创建一个Selector
            Selector selector = Selector.open();
            /** 
            必须将channel注册到selector上,并订阅OP_ACCEPT事件
             		SelectionKey.OP_CONNECT  channel成功连接到另一个服务器称为"连接就绪"
					SelectionKey.OP_ACCEPT	 server socket channel准备好接收新进入的连接称为"接收就绪"
					SelectionKey.OP_READ	 有数据可读的通道可以说是"读就绪"
					SelectionKey.OP_WRITE	 有数据可写的通道可以说是"读就绪"
			*/
            serverChannel.register(selector, SelectionKey.OP_ACCEPT);

            while (true) {
            	// 返回你所感兴趣的事件(连接,接受,读或写)已经准备就绪的那些通道
                int readyChannels = selector.select();
                if (readyChannels == 0){
                	continue;
                }
                // 访问"已选择键集"中的就绪通道
                Set<SelectionKey> selectedKeys = selector.selectedKeys();
                
                // 可以遍历这个已选择的集合来访问就绪的通道
                Iterator<SelectionKey> keyIterator = selectedKeys.iterator();
                while (keyIterator.hasNext()) {
                    SelectionKey key = keyIterator.next();
                    // 注意每次迭代末尾的remove()调用,Selector不会自己从已选择集中移除SelectioKey实例,必须在处理完通道时自己移除。
                    keyIterator.remove();

					// 一个server socket channel准备号接收新进入的连接称为"接收就绪"
                    if (key.isAcceptable()) {
                        ServerSocketChannel server = (ServerSocketChannel) key.channel();
                        // 客户端socker注册进来
                        SocketChannel clientChannel = server.accept();
                        clientChannel.configureBlocking(false);
                        clientChannel.register(selector, SelectionKey.OP_READ);
                        // 客户端通道是否有数据流进来
                    } else if (key.isReadable()) {
                        SocketChannel clientChannel = (SocketChannel) key.channel();
                        ByteBuffer buffer = ByteBuffer.allocate(1024);
                        clientChannel.read(buffer);
                        buffer.flip();
                        byte[] bytes = new byte[buffer.remaining()];
                        buffer.get(bytes);
                        String request = new String(bytes);

                        String response = "HTTP/1.1 200 OK\r\n\r\nHello, World!\r\n";
                        ByteBuffer responseBuffer = ByteBuffer.wrap(response.getBytes());
                        clientChannel.write(responseBuffer);
                        clientChannel.close();
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

3. 模型

上述代码结合该模型,第二次阅读代码,会有更深的理解

4. 原理

多路复用才是NIO不阻塞的原因

相关推荐
刘白Live18 分钟前
【Java】谈一谈浅克隆和深克隆
java
一线大码20 分钟前
项目中怎么确定线程池的大小
java·后端
要加油哦~23 分钟前
vue · 插槽 | $slots:访问所有命名插槽内容 | 插槽的使用:子组件和父组件如何书写?
java·前端·javascript
crud26 分钟前
Spring Boot 3 整合 Swagger:打造现代化 API 文档系统(附完整代码 + 高级配置 + 最佳实践)
java·spring boot·swagger
天天摸鱼的java工程师31 分钟前
从被测试小姐姐追着怼到运维小哥点赞:我在项目管理系统的 MySQL 优化实战
java·后端·mysql
周某某~43 分钟前
四.抽象工厂模式
java·设计模式·抽象工厂模式
异常君1 小时前
高并发数据写入场景下 MySQL 的性能瓶颈与替代方案
java·mysql·性能优化
烙印6011 小时前
MyBatis原理剖析(二)
java·数据库·mybatis
你是狒狒吗1 小时前
TM中,return new TransactionManagerImpl(raf, fc);为什么返回是new了一个新的实例
java·开发语言·数据库
勤奋的知更鸟1 小时前
Java编程之组合模式
java·开发语言·设计模式·组合模式