一、前言
在学习 Netty 框架之前,我们需要先理解 Java 的 IO 和 NIO 模型。这两种模型在网络编程中扮演着重要角色,而 Netty 则是基于 NIO 模型构建的高性能网络应用框架。本文将详细介绍 IO 和 NIO 的概念、特点以及它们之间的区别,为后续学习 Netty 打下坚实基础。
二、IO 和 NIO
下表总结了 Java IO 和 NIO 之间的主要区别:
IO 与 NIO 的对比
特性 | IO | NIO |
---|---|---|
处理方式 | 阻塞 | 非阻塞 |
数据处理 | 面向流 | 面向缓冲 |
传输方式 | 单向 | 双向(通过 Channel) |
并发处理 | 多线程 | 单线程可处理多连接 |
编程难度 | 简单 | 相对复杂 |
适用场景 | 连接数少,流程简单 | 高并发,大量连接 |
2.1. 阻塞与非阻塞
阻塞式操作:当一个线程调用 read() 或 write() 时,该线程被阻塞,直到有数据可读或数据完全写入。
非阻塞操作:线程可以发起 IO 请求后立即返回,做其他事情。
Java IO是属于阻塞式,NIO属于非阻塞式操作。阻塞式最大的缺点就是导致资源利用率不高。
2.2.面向流(Stream)和面向缓冲(Buffer)
面向流:IO 是面向流的,每次从流中读取一个或多个字节,直至读取所有字节。
面向缓冲:NIO 是面向缓冲区的,数据被读取到一个缓冲区中,便于处理。
下表总结了 面向流 和 面向缓冲的主要区别:
特性 | 面向流 | 面向缓冲 |
---|---|---|
数据处理单位 | 字节流 | 缓冲区 |
数据读写方式 | 从流中顺序读取 | 从缓冲区中读取或写入 |
处理灵活性 | 只能顺序读写 | 可以随机访问缓冲区的任何位置 |
数据缓存 | 不直接缓存数据 | 数据缓存在缓冲区中 |
适用场景 | 简单的顺序读写 | 需要灵活处理的数据 |
读写操作 | read()或write()方法 | flip()、clear()等缓冲区操作 |
处理效率 | 相对较低 | 较高,特别是在大量数据处理时 |
内存使用 | 通常较少 | 可能较多,需要分配缓冲区 |
编程复杂度 | 相对简单 | 较复杂,需要管理缓冲区状态 |
数据转换 | 需要为不同数据类型创建不同的流 | 可以使用不同的缓冲区类型(如ByteBuffer、CharBuffer) |
三、同步和异步
在这里提一嘴同步和异步。
同步:在同步操作中,发起调用的线程会等待操作完成后才继续执行。
异步:在异步操作中,发起调用的线程不会等待操作完成,而是继续执行其他任务。操作完成后通过回调、通知等机制来通知调用方。
在这里用我们熟悉的表格来总结一下IO同步异步的差别:
特性 | 同步阻塞 IO | 同步非阻塞 IO | 异步 IO |
---|---|---|---|
阻塞性 | 阻塞 | 非阻塞 | 非阻塞 |
CPU 利用 | 低 | 高(轮询消耗 CPU) | 高 |
编程复杂度 | 简单 | 复杂 | 较复杂 |
适用场景 | 连接数少 | 高并发 | 高并发,长连接 |
四、小试牛刀
光练不说傻把式,又练又说真把式,那么接下来我们搭建一下来试试IO和NIO的差别,在实操的感受过程中,再想一下为什么有了NIO还有Netty的诞生呢?
IO初体验
4.1. 服务端代码
/**
* @Author: Panjiangfeng
* @CreateTime: 2024-10-11 23:23
* @Description: IOClient
* @Version: 1.0
*/
public class IOClient {
public static void main(String[] args) {
new Thread(() -> {
try {
Socket socket = new Socket("127.0.0.1", 8000);
while (true) {
try {
// 发送消息到服务端
socket.getOutputStream().write((new Date() + ": hello world").getBytes());
// 休眠2秒
Thread.sleep(2000);
} catch (Exception e) {
e.printStackTrace(); // 打印异常
}
}
} catch (