【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 事件监听、多路复用等进阶内容。

相关推荐
Arenaschi7 小时前
关于GPT的版特点
java·网络·人工智能·windows·python·gpt
人道领域7 小时前
【LeetCode刷题日记】108.将有序数组转换为二叉搜索树
java·算法·leetcode
右耳朵猫AI7 小时前
Rust技术周刊 2026年第19周
开发语言·后端·rust
橙淮7 小时前
并发编程(五)
java
Leweslyh7 小时前
基于 Confucius 架构的无人集群网络控制原语解析
开发语言·网络·php
过期动态7 小时前
【LeetCode 热题 100】无重复字符的最长子串
java·数据结构·spring boot·算法·leetcode·职场和发展
Yeats_Liao7 小时前
好复杂的 IoT 世界:工业数据采集技术栈全景解析
java·物联网·struts
月落归舟7 小时前
Java线程小记
java·开发语言
西凉的悲伤7 小时前
Spring Cloud Gateway介绍
java·spring cloud·gateway
摇滚侠7 小时前
01 基础语法 JavaScript 入门到精通全套教程
开发语言·javascript·ecmascript