【Day21】NIO入门:通道、缓冲区与非阻塞IO基础

学习 NIO 入门知识,重点了解通道(Channel)、缓冲区(Buffer)以及非阻塞 IO 的基础概念和用法。

一、NIO 核心概念总览

Java NIO(Non-blocking IO,非阻塞 IO)是 JDK 1.4 引入的新 IO 模型,核心是 缓冲区(Buffer)通道(Channel)选择器(Selector),解决了传统 BIO(阻塞 IO)效率低、资源占用高的问题。

  • 传统 BIO:以 "流" 为核心,数据单向传输(输入流 / 输出流),且读写阻塞;
  • NIO:以 "缓冲区" 为核心,数据先写入缓冲区再读取,通道双向传输,支持非阻塞模式。

二、缓冲区(Buffer):数据的容器

  1. 核心定义

Buffer 是一块内存区域,用于临时存储数据,所有 NIO 操作都通过 Buffer 完成(而非直接操作流)。常见的 Buffer 类型:

类型 对应基本数据类型
ByteBuffer byte(最常用)
CharBuffer char
IntBuffer int
LongBuffer long
FloatBuffer float
DoubleBuffer double
  1. Buffer 核心属性(必须掌握)
  • capacity:缓冲区总容量(创建后不可变);
  • position:当前操作的位置(初始为 0,读写时自动移动);
  • limit:可操作数据的上限(写模式下 = capacity,读模式下 = 写时的 position);
  • mark :标记位,可通过 mark() 标记位置,reset() 恢复到该位置。
  1. Buffer 核心操作示例(ByteBuffer)

java

运行

java 复制代码
import java.nio.ByteBuffer;

public class BufferDemo {
    public static void main(String[] args) {
        // 1. 创建缓冲区:容量为 10 个字节
        ByteBuffer buffer = ByteBuffer.allocate(10);
        System.out.println("初始化状态:");
        printBuffer(buffer); // capacity=10, position=0, limit=10

        // 2. 写入数据(写模式)
        String data = "NIO";
        buffer.put(data.getBytes());
        System.out.println("\n写入数据后(写模式):");
        printBuffer(buffer); // capacity=10, position=3, limit=10

        // 3. 切换为读模式(flip():limit=position,position=0)
        buffer.flip();
        System.out.println("\n切换读模式后:");
        printBuffer(buffer); // capacity=10, position=0, limit=3

        // 4. 读取数据
        byte[] readData = new byte[buffer.limit()];
        buffer.get(readData);
        System.out.println("\n读取的数据:" + new String(readData)); // 输出 NIO
        System.out.println("读取后状态:");
        printBuffer(buffer); // capacity=10, position=3, limit=3

        // 5. 清空缓冲区(clear():position=0, limit=capacity,数据未删除,只是重置标记)
        buffer.clear();
        System.out.println("\n清空后状态:");
        printBuffer(buffer); // capacity=10, position=0, limit=10
    }

    // 辅助方法:打印 Buffer 状态
    private static void printBuffer(ByteBuffer buffer) {
        System.out.printf("capacity: %d, position: %d, limit: %d%n",
                buffer.capacity(), buffer.position(), buffer.limit());
    }
}
  1. 关键方法解释
  • allocate(int capacity):创建堆内存缓冲区 (常用);allocateDirect(int capacity):创建直接内存缓冲区(效率更高,无需拷贝,但创建 / 销毁成本高);
  • put():向缓冲区写入数据(写模式);
  • flip():切换为读模式(核心);
  • get():从缓冲区读取数据(读模式);
  • clear():重置标记(并非删除数据),准备再次写入;
  • rewind():重置 position 为 0,可重复读取数据;
  • compact():将未读取的剩余数据移到缓冲区开头,切换为写模式(适合分批处理数据)。

三、通道(Channel):数据的传输通道

  1. 核心定义

Channel 是连接缓冲区和数据源(文件、网络)的双向通道,数据必须通过 Channel 读取 / 写入,且 Channel 支持非阻塞模式。

  1. 常见 Channel 类型
类型 用途
FileChannel 文件读写(阻塞 / 非阻塞)
SocketChannel TCP 客户端通道
ServerSocketChannel TCP 服务端通道
DatagramChannel UDP 数据报通道
  1. FileChannel 示例(文件读写)

java

运行

java 复制代码
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

public class ChannelDemo {
    public static void main(String[] args) throws Exception {
        // 1. 准备文件:从 input.txt 读取,写入 output.txt
        String inputPath = "input.txt"; // 需提前创建,写入任意内容
        String outputPath = "output.txt";

        // 2. 创建输入/输出流,获取 FileChannel
        try (FileInputStream fis = new FileInputStream(inputPath);
             FileOutputStream fos = new FileOutputStream(outputPath);
             FileChannel inChannel = fis.getChannel();
             FileChannel outChannel = fos.getChannel()) {

            // 3. 创建缓冲区
            ByteBuffer buffer = ByteBuffer.allocate(1024);

            // 4. 读取数据到缓冲区
            while (inChannel.read(buffer) != -1) { // read() 返回 -1 表示读取完毕
                buffer.flip(); // 切换为读模式
                outChannel.write(buffer); // 将缓冲区数据写入输出通道
                buffer.clear(); // 清空缓冲区,准备下一次读取
            }
        }
        System.out.println("文件复制完成!");
    }
}
  1. 关键方法解释
  • read(Buffer buffer):从通道读取数据到缓冲区,返回读取的字节数,-1 表示读取完毕;
  • write(Buffer buffer):将缓冲区数据写入通道;
  • transferFrom(Channel src, long position, long count):直接从源通道复制数据(零拷贝,效率极高);
  • transferTo(long position, long count, Channel target):直接向目标通道复制数据(零拷贝)。

四、非阻塞 IO 基础(核心优势)

  1. 非阻塞 IO 定义

传统 BIO 中,线程调用 read()/write() 时会阻塞,直到数据读写完成;NIO 的非阻塞模式下,线程调用读写方法后,无论数据是否就绪,都会立即返回,线程可处理其他任务,避免资源浪费。

  1. 非阻塞 IO 核心:选择器(Selector)

Selector 是 NIO 实现非阻塞的核心,一个线程可通过 Selector 监听多个 Channel 的事件(连接、读、写),实现 "单线程管理多通道",大幅减少线程开销。

  1. 非阻塞 SocketChannel 简单示例

java

运行

java 复制代码
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;

public class NonBlockingClient {
    public static void main(String[] args) throws Exception {
        // 1. 创建 SocketChannel 并设置为非阻塞模式
        SocketChannel socketChannel = SocketChannel.open();
        socketChannel.configureBlocking(false);

        // 2. 连接服务器(非阻塞:立即返回,不管是否连接成功)
        socketChannel.connect(new InetSocketAddress("localhost", 8080));

        // 3. 等待连接完成
        while (!socketChannel.finishConnect()) {
            // 连接未就绪时,线程可处理其他任务
            System.out.println("连接中,处理其他任务...");
            Thread.sleep(100);
        }

        // 4. 写入数据
        String msg = "Hello NIO Non-blocking!";
        ByteBuffer buffer = ByteBuffer.wrap(msg.getBytes());
        socketChannel.write(buffer);

        // 5. 读取响应(非阻塞:无数据时返回 0,不会阻塞)
        buffer.clear();
        int readBytes = socketChannel.read(buffer);
        if (readBytes > 0) {
            buffer.flip();
            byte[] response = new byte[buffer.limit()];
            buffer.get(response);
            System.out.println("服务器响应:" + new String(response));
        } else if (readBytes == 0) {
            System.out.println("暂无服务器响应,继续处理其他任务...");
        }

        // 6. 关闭通道
        socketChannel.close();
    }
}

五、BIO vs NIO 核心区别

特性 BIO NIO
核心单位 流(Stream) 缓冲区(Buffer)
传输方向 单向(输入 / 输出流) 双向(Channel)
阻塞模式 仅阻塞 支持阻塞 / 非阻塞
线程模型 一连接一线程 单线程管理多连接
核心组件 流、线程 Buffer、Channel、Selector

总结

  1. Buffer 是 NIO 的数据容器,核心是 capacity/position/limit 三个属性,flip() 是读写模式切换的关键;
  2. Channel 是双向数据通道,所有 NIO 数据传输必须通过 Channel,FileChannel 支持零拷贝提升效率;
  3. 非阻塞 IO 是 NIO 的核心优势,通过 Selector 实现单线程管理多通道,解决 BIO 线程开销大的问题。

掌握这三个核心概念,就理解了 NIO 的基础框架,后续可深入学习 Selector 事件监听、多路复用等进阶内容。

相关推荐
ziyue75752 小时前
idea将配置移动到自定义位置
java·intellij-idea·idea·软件
quant_19862 小时前
BTC 行情预警系统实战教程
开发语言·后端·python·websocket·程序人生·金融
南汐以墨2 小时前
UI自动化测试指南(一):浅解概念
java·测试工具
世转神风-2 小时前
qt-基础打印-不换行打印
开发语言·qt
不能只会打代码2 小时前
力扣--1970. 你能穿过矩阵的最后一天(Java)
java·算法·leetcode·二分查找·力扣·bfs·最后可行时间
少年执笔2 小时前
android新版TTS无法进行语音播报
android·java
Ralph_Y2 小时前
C++数据库操作
开发语言·数据库·c++
superman超哥2 小时前
Rust 日志级别与结构化日志:生产级可观测性实践
开发语言·后端·rust·可观测性·rust日志级别·rust结构化日志
咸鱼2.02 小时前
【java入门到放弃】数据结构
java·开发语言·数据结构