NIO知识点
一、Java NIO 基础概念
Java NIO(New Input/Output)是从 Java 1.4 版本开始引入的新的 IO API,它提供了与标准 IO 不同的工作方式。主要特点包括:
- 面向缓冲区:数据读取到一个稍后处理的缓冲区,需要时可在缓冲区中前后移动,增加了处理过程中的灵活性。
- 非阻塞 IO:允许线程在等待数据时执行其他任务,提高了系统的吞吐量和响应性。
- 选择器:一个选择器可以监听多个通道的事件(如连接打开、数据到达等),从而让一个线程可以处理多个通道。
二、核心组件详解
1. 缓冲区(Buffer)
缓冲区是一个用于存储特定基本数据类型的容器,所有缓冲区都具有以下属性:
- capacity:容量,即缓冲区能够容纳的数据元素的最大数量,在创建时确定且不能更改。
- position:位置,下一个要读取或写入的数据元素的索引。
- limit:限制,缓冲区中可以读取或写入的最大位置,位于 limit 后的数据不可读写。
- mark:标记,一个备忘位置,调用 mark () 方法将 mark 设为当前的 position 值,之后可通过 reset () 方法将 position 恢复到标记的位置。
常用方法:
allocate(int capacity)
:创建一个指定容量的缓冲区put(data)
:向缓冲区写入数据flip()
:切换到读模式(将 limit 设为 position,position 设为 0)get()
:从缓冲区读取数据clear()
:清空缓冲区(将 position 设为 0,limit 设为 capacity)rewind()
:重置缓冲区(将 position 设为 0,mark 被丢弃)
示例代码:
import java.nio.IntBuffer;public class BufferExample {public static void main(String[] args) {// 创建一个容量为10的IntBufferIntBuffer buffer = IntBuffer.allocate(10);// 向缓冲区写入数据for (int i = 0; i < 5; i++) {buffer.put(i);}// 切换到读模式buffer.flip();// 从缓冲区读取数据while (buffer.hasRemaining()) {System.out.print(buffer.get() + " ");}System.out.println();// 重置缓冲区,以便再次读取buffer.rewind();// 再次读取数据System.out.println("Rewind后再次读取:");while (buffer.hasRemaining()) {System.out.print(buffer.get() + " ");}// 清空缓冲区buffer.clear();System.out.println("\nclear后position: " + buffer.position());System.out.println("clear后limit: " + buffer.limit());}
}
2. 通道(Channel)
通道是对原 I/O 包中的流的模拟,可以通过它读取和写入数据。与流不同的是,通道是双向的,支持异步读写操作,且通道始终与缓冲区交互。
常用通道实现:
FileChannel
:用于文件读写SocketChannel
:用于 TCP 网络通信的客户端ServerSocketChannel
:用于 TCP 网络通信的服务器端DatagramChannel
:用于 UDP 网络通信
示例代码:使用 FileChannel 复制文件
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;public class FileChannelExample {public static void main(String[] args) {try (FileInputStream fis = new FileInputStream("source.txt");FileOutputStream fos = new FileOutputStream("destination.txt");FileChannel inChannel = fis.getChannel();FileChannel outChannel = fos.getChannel()) {// 创建缓冲区ByteBuffer buffer = ByteBuffer.allocate(1024);// 从输入通道读取数据到缓冲区while (inChannel.read(buffer) != -1) {// 切换到读模式buffer.flip();// 将缓冲区的数据写入输出通道outChannel.write(buffer);// 清空缓冲区,准备下一次读取buffer.clear();}System.out.println("文件复制完成");} catch (IOException e) {e.printStackTrace();}}
}
3. 选择器(Selector)
选择器是 Java NIO 中实现非阻塞 IO 的核心组件,它允许一个线程处理多个通道的事件。通过使用选择器,线程可以监听多个通道的连接、数据到达等事件,从而高效地管理多个通道。
使用步骤:
- 创建 Selector:
Selector selector = Selector.open();
- 将通道注册到选择器:
channel.configureBlocking(false);
SelectionKey key = channel.register(selector, SelectionKey.OP_READ);
- 轮询选择器:
selector.select();
- 获取就绪的事件:
Set<SelectionKey> selectedKeys = selector.selectedKeys();
- 处理事件:遍历 selectedKeys,根据 key 的类型处理相应的事件
示例代码:使用 Selector 实现简单的非阻塞服务器
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
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 NioServerExample {private static final int PORT = 8080;public static void main(String[] args) {try (Selector selector = Selector.open();ServerSocketChannel serverSocketChannel = ServerSocketChannel.open()) {// 配置服务器套接字通道serverSocketChannel.socket().bind(new InetSocketAddress(PORT));serverSocketChannel.configureBlocking(false);// 将服务器套接字通道注册到选择器,监听接受连接事件serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);System.out.println("服务器启动,监听端口: " + PORT);// 轮询选择器while (true) {// 阻塞等待就绪的通道selector.select();// 获取就绪的事件集合Set<SelectionKey> selectedKeys = selector.selectedKeys();Iterator<SelectionKey> keyIterator = selectedKeys.iterator();// 处理每个就绪的事件while (keyIterator.hasNext()) {SelectionKey key = keyIterator.next();// 处理接受连接事件if (key.isAcceptable()) {ServerSocketChannel ssc = (ServerSocketChannel) key.channel();SocketChannel socketChannel = ssc.accept();System.out.println("接受新连接: " + socketChannel);// 配置客户端通道为非阻塞模式,并注册到选择器,监听读取事件socketChannel.configureBlocking(false);socketChannel.register(selector, SelectionKey.OP_READ);}// 处理读取事件else if (key.isReadable()) {SocketChannel socketChannel = (SocketChannel) key.channel();ByteBuffer buffer = ByteBuffer.allocate(1024);// 读取客户端数据int bytesRead = socketChannel.read(buffer);if (bytesRead > 0) {buffer.flip();byte[] data = new byte[buffer.remaining()];buffer.get(data);String message = new String(data, "UTF-8");System.out.println("收到客户端消息: " + message);// 回写响应给客户端String response = "服务器已收到消息: " + message;ByteBuffer responseBuffer = ByteBuffer.wrap(response.getBytes());socketChannel.write(responseBuffer);} else if (bytesRead == -1) {// 客户端关闭连接System.out.println("客户端关闭连接: " + socketChannel);socketChannel.close();}}// 移除已处理的事件keyIterator.remove();}}} catch (IOException e) {e.printStackTrace();}}
}
三、非阻塞 IO 编程实践
1. 非阻塞客户端实现
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.util.Scanner;public class NioClientExample {private static final String SERVER_HOST = "localhost";private static final int SERVER_PORT = 8080;public static void main(String[] args) {try (SocketChannel socketChannel = SocketChannel.open();Scanner scanner = new Scanner(System.in)) {// 连接服务器socketChannel.connect(new InetSocketAddress(SERVER_HOST, SERVER_PORT));System.out.println("已连接到服务器: " + socketChannel);// 配置为非阻塞模式socketChannel.configureBlocking(false);// 创建缓冲区ByteBuffer buffer = ByteBuffer.allocate(1024);// 读取用户输入并发送给服务器while (true) {System.out.print("请输入要发送的消息(输入exit退出): ");String message = scanner.nextLine();if ("exit".equalsIgnoreCase(message)) {break;}// 发送消息给服务器buffer.clear();buffer.put(message.getBytes("UTF-8"));buffer.flip();socketChannel.write(buffer);// 接收服务器响应buffer.clear();int bytesRead = socketChannel.read(buffer);if (bytesRead > 0) {buffer.flip();byte[] data = new byte[buffer.remaining()];buffer.get(data);String response = new String(data, "UTF-8");System.out.println("收到服务器响应: " + response);}}} catch (IOException e) {e.printStackTrace();}}
}
2. 基于选择器的多客户端处理
上面的 NioServerExample 已经展示了基于选择器的服务器实现,它可以同时处理多个客户端连接。关键点在于:
- 服务器通道注册 OP_ACCEPT 事件以接受新连接
- 客户端通道注册 OP_READ 事件以读取客户端数据
- 单个线程通过 Selector 轮询所有注册的通道,处理就绪的事件
四、NIO 高级应用
1. 文件锁定
Java NIO 提供了文件锁定机制,可以锁定文件的一部分或全部,防止其他程序访问。
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;public class FileLockingExample {public static void main(String[] args) {try (RandomAccessFile file = new RandomAccessFile("test.txt", "rw");FileChannel channel = file.getChannel()) {// 获取文件锁(锁定整个文件)FileLock lock = channel.lock();System.out.println("文件已锁定");// 执行文件操作// ...// 释放锁lock.release();System.out.println("文件锁已释放");} catch (IOException e) {e.printStackTrace();}}
}
2. 内存映射文件
内存映射文件允许将文件的一部分或全部映射到内存中,这样可以像访问内存一样访问文件,提高 IO 效率。
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;public class MemoryMappedFileExample {private static final int SIZE = 1024 * 1024; // 1MBpublic static void main(String[] args) {try (RandomAccessFile file = new RandomAccessFile("mapped_file.dat", "rw");FileChannel channel = file.getChannel()) {// 创建内存映射文件MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_WRITE, 0, SIZE);// 写入数据for (int i = 0; i < SIZE; i++) {buffer.put((byte) 'A');}// 读取数据buffer.position(0);for (int i = 0; i < 10; i++) {System.out.print((char) buffer.get());}System.out.println();System.out.println("内存映射文件操作完成");} catch (IOException e) {e.printStackTrace();}}
}
3. 异步 IO(AIO)
Java 7 引入了 NIO.2,提供了真正的异步 IO 支持,通过 CompletionHandler 回调或 Future 对象获取操作结果。
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousServerSocketChannel;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
import java.util.concurrent.Future;public class AioServerExample {private static final int PORT = 8090;public static void main(String[] args) throws IOException, InterruptedException {// 创建异步服务器套接字通道AsynchronousServerSocketChannel serverChannel = AsynchronousServerSocketChannel.open().bind(new InetSocketAddress(PORT));System.out.println("异步服务器启动,监听端口: " + PORT);// 接受客户端连接serverChannel.accept(null, new CompletionHandler<AsynchronousSocketChannel, Void>() {@Overridepublic void completed(AsynchronousSocketChannel clientChannel, Void attachment) {// 继续接受下一个连接serverChannel.accept(null, this);// 处理当前连接handleClient(clientChannel);}@Overridepublic void failed(Throwable exc, Void attachment) {exc.printStackTrace();}});// 保持主线程运行Thread.sleep(Long.MAX_VALUE);}private static void handleClient(AsynchronousSocketChannel clientChannel) {try {System.out.println("接受新连接: " + clientChannel.getRemoteAddress());// 创建缓冲区ByteBuffer buffer = ByteBuffer.allocate(1024);// 异步读取数据clientChannel.read(buffer, buffer, new CompletionHandler<Integer, ByteBuffer>() {@Overridepublic void completed(Integer bytesRead, ByteBuffer buffer) {if (bytesRead > 0) {buffer.flip();byte[] data = new byte[buffer.remaining()];buffer.get(data);String message = new String(data);System.out.println("收到客户端消息: " + message);// 回写响应String response = "服务器已收到: " + message;ByteBuffer responseBuffer = ByteBuffer.wrap(response.getBytes());// 异步写入响应Future<Integer> writeResult = clientChannel.write(responseBuffer);try {writeResult.get(); // 等待写入完成System.out.println("响应已发送给客户端");} catch (Exception e) {e.printStackTrace();}} else if (bytesRead == -1) {// 客户端关闭连接try {System.out.println("客户端关闭连接: " + clientChannel.getRemoteAddress());clientChannel.close();} catch (IOException e) {e.printStackTrace();}}}@Overridepublic void failed(Throwable exc, ByteBuffer buffer) {try {System.out.println("读取失败,关闭连接: " + clientChannel.getRemoteAddress());clientChannel.close();} catch (IOException e) {e.printStackTrace();}}});} catch (IOException e) {e.printStackTrace();}}
}
五、NIO 性能优化建议
-
合理设置缓冲区大小:过小的缓冲区会增加 IO 操作次数,过大的缓冲区会浪费内存。
-
避免不必要的上下文切换:非阻塞 IO 的优势在于一个线程可以处理多个通道,避免创建过多线程导致上下文切换开销。
-
使用直接缓冲区:对于需要频繁进行 IO 操作的数据,使用直接缓冲区(
ByteBuffer.allocateDirect()
)可以减少内存拷贝。 -
优化选择器配置:避免在选择器上注册过多通道,可根据系统资源和负载情况进行合理划分。
-
合理处理并发:在多线程环境下,注意对共享资源的同步访问,避免数据竞争。
相关文章:
NIO知识点
一、Java NIO 基础概念 Java NIO(New Input/Output)是从 Java 1.4 版本开始引入的新的 IO API,它提供了与标准 IO 不同的工作方式。主要特点包括: 面向缓冲区:数据读取到一个稍后处理的缓冲区,需要时可在…...

T/CCSA 663-2025《医疗科研云平台技术要求》标准解读与深度分析
参考地址:https://www.doc88.com/p-30280431175529.html 引言 随着医疗信息化建设的深入推进,医疗行业正经历从"业务驱动"向"数据驱动"的转型。在这一背景下,中国通信标准化协会(CCSA)于2025年发布了T/CCSA 663-2025《医疗科研云平台技术要求》标准,并…...

win11回收站中出现:查看回收站中是否有以下项: WPS云盘回收站
好久没更新了,首先祝所有大朋友、小朋友六一儿童节快乐,真的希望我们永远都不会长大呀,长大真的好累呀(•_•) 免责声明 笔者先来个免责声明吧,被网上的阴暗面吓到了 若读者参照笔者的这篇文章所执行的操作中途或后续出现的任何…...
Nginx+Tomcat 负载均衡群集
一、Tomcat 基础与案例部署 (一)Tomcat 概述与应用场景 起源与命名 Tomcat 最初由 Sun 公司的詹姆斯・邓肯・戴维森开发,后贡献给 Apache 软件基金会。项目早期名为 “Catalina”,因此安装目录中包含大量与 Catalina 相关的文件…...

SCDN如何同时保障网站加速与DDoS防御?
在互联网时代,网站既要面对用户访问量的激增,又要抵御层出不穷的网络攻击,特别是DDoS攻击的威胁。SCDN(安全内容分发网络)作为融合加速与安全的解决方案,如何实现“加速”与“防御”的双重保障?…...
Trae CN IDE 中 Python 开发的具体流程和配置总结
以下是 Trae CN IDE 中 Python 开发的具体流程和配置总结,结合实例说明,帮助开发者快速上手: 一、环境准备 1. 安装 Trae CN IDE 下载地址:访问 Trae 官网 下载对应操作系统的安装包(Windows .exe / macOS .dmg / Linux .tar.gz)。安装步骤: Windows:双击 .exe 文件,…...
PostgreSQL不同的等级认证体系
PostgreSQL 专家认证有不同的等级和体系,以工业和信息化部人才交流中心推出的认证为例,分为 PGCA 认证专员、PGCP 认证专家、PGCM 认证大师三个等级。以下是学习建议: 明确学习目标与认证等级 PGCA初级认证专员:适合刚接触 Post…...

项目前置知识——不定参以及设计模式
1.C语言不定参宏函数 c语言中,printf就是一个不定参函数,在使用不定参宏函数时,我们使用__VA_ARGS__来解析不定参: #include <iostream> #include <cstdarg>#define LOG(fmt/*格式*/, .../*用...表示不定参*/) prin…...

04powerbi-度量值-筛选引擎CALCULATE()
1、calculate calculate 的参数分两部分,分别是计算器和筛选器 2、多条件calculater与表筛选 多条件有不列的多条件 相同列的多条件 3、calculatertable (表,筛选条件)表筛选 与calculate用法一样,可以用创建表&…...
JavaScript排序算法详解:从基础到高级
排序是编程中最基本也是最重要的操作之一。JavaScript作为一门广泛应用于Web开发的语言,提供了内置的排序方法,但了解各种排序算法的原理和实现对于开发者来说仍然至关重要。本文将深入探讨JavaScript中常见的排序算法,帮助您理解它们的原理、…...

chromedriver 下载失败
问题描述 chromedriver 2.46.0 下载失败 淘宝https://registry.npmmirror.com/chromedriver/2.46/chromedriver_win32.zip无法下载 解决方法 找到可下载源 https://cdn.npmmirror.com/binaries/chromedriver/2.46/chromedriver_win32.zip ,先将其下载到本地目录(D…...

Weather app using Django - Python
我们的任务是使用 Django 创建一个 Weather 应用程序,让用户可以输入城市名称并查看当前天气详细信息,例如温度、湿度和压力。我们将通过设置一个 Django 项目,创建一个视图来从 OpenWeatherMap API 获取数据,并设计一个简单的模板…...

机器视觉2,硬件选型
机器视觉1,学习了硬件的基本知识和选型,现在另外的教材巩固知识 选相机 工业相机选型的保姆级教程_哔哩哔哩_bilibili 1.先看精度多少mm,被检测物体长宽多少mm》分辨率, 选出合理范围内的相机 2.靶面尺寸,得出分…...
自定义序列生成器之单体架构实现
主键 ID VS 业务 ID 在数据库设计中,除了主键 ID,一般还需要一个具有唯一索引的业务 ID。二者承担的职责不一样,它们共同满足了我们对于 技术实现 和 业务需求 的双重目标 1. 职责分离原则 主键 ID 业务唯一标识 ID 作用 保证数据库层面…...

电阻电容的选型
一、电阻选型 1.1安装方式 贴片电阻体积小,适用于SMT生产;功率小;易拆解插件电阻体积大;功率大;不易脱落 1.2阻值 电阻的阻值是离散的,其标称阻值根据精度分为E6、E12、E24、E48、E96、E192六大系列&am…...

12.springCloud AlibabaSentinel实现熔断与限流
目录 一、Sentinel简介 1.官网 2.Sentinel 是什么 3.Sentinel 的历史 4.Sentinel 基本概念 资源 规则 5.Sentinel 功能和设计理念 (1).流量控制 什么是流量控制 流量控制设计理念 (2).断降级 什么是熔断降级 熔断降级设计理念 (3).系统自适应保护 6.主要工作机制…...
Cookie 和 Session:Web 身份验证的核心机制
文章目录 一、Cookie:客户端存储的小数据块**核心特性****典型应用场景**二、Session:服务器端的会话存储**核心特性****典型应用场景**三、Cookie vs Session:核心区别对比四、最佳实践与扩展 一、Cookie:客户端存储的小数据块 …...

vSOME/IP与ETAS DSOME/IP通信的问题解决方案
✅ 一、服务版本不匹配导致 Handover 问题 —— 需要更新 VSOMEIP 代码逻辑 📌 问题描述: 在 SOME/IP 通信中,发布者(offer)与订阅者(subscribe)之间存在服务版本不一致的问题,导致 Handover(切换)失败。 ✅ 解决方案: 需要在 offer_service 和 subscribe 接口中…...
修改vscode切换上一个/下一个标签页快捷键
装了vim后一直没找到切tab页的快捷键 Code>Preferences>Keyboard Shortcuts on macOS 搜索这2个选项 我设置成了commandh 向前切换,commandl向后切换,贴合vim的方向设置 workbench.action.previousEditor commandh workbench.action.nextEdit…...
三大中文wordpress原创主题汉主题
汉主题 汉主题是一款极具特色的 WordPress 主题,由国内专业团队精心打造,专为中文用户设计。其设计灵感源自博大精深的汉文化,将传统文化元素与现代网页设计理念巧妙融合,呈现出独特而典雅的风格。无论是用于个人博客展示文学创作…...

软考-系统架构设计师-第十五章 信息系统架构设计理论与实践
信息系统架构设计理论与实践 15.2 信息系统架构风格和分类15.3 信息系统常用的架构模型15.4 企业信息系统总体框架15.5 信息系统架构设计方法 15.2 信息系统架构风格和分类 信息系统架构风格 数据流体系结构风格:批处理、管道-过滤器调用/返回体系结构风格&#x…...
Redis缓存-数据淘汰策略
数据淘汰策略就是,当redis内存满的时候,此时在向redis添加新的key,那么redis会按照某一种规则将内存中的数据删掉,这种删除数据的规则成为内存的淘汰策略。 redis支持8中淘汰策略 1.noeviction,这种是redis默认的情况…...
52. N 皇后 II【 力扣(LeetCode) 】
文章目录 零、原题链接一、题目描述二、测试用例三、解题思路四、参考代码 零、原题链接 52. N 皇后 II 一、题目描述 n 皇后问题 研究的是如何将 n 个皇后放置在 n n 的棋盘上,并且使皇后彼此之间不能相互攻击。【补充:不能互相攻击就是要求一个皇后的…...

MySQL 8 完整安装指南(Ubuntu 22.04)
MySQL 8 完整安装指南(Ubuntu 22.04) 本教程详细说明如何在 Ubuntu 22.04 上安装和配置 MySQL 8,包含安全优化及远程访问设置。 1️⃣ 添加 MySQL 官方 APT 仓库 官网仓库下载地址:MySQL APT 仓库下载页 下载仓库配置包&#…...
C++ 标准输入输出 -- <iostream>
<iostream>库是 C++ 标准库中用于输入输出操作的头文件。 <iostream> 定义了几个常用的流类和操作符,允许程序与标准输入输出设备(如键盘和屏幕)进行交互。 以下是<iostream>库的详细使用说明,包括其主要类和常见用法示例。 主要类 std::istream:用于…...
记一次sql按经纬度计算距离
具体代码: ROUND函数在mysql可以用来计算经纬度,代码如下: SELECTa.store_name_sfa as storeName,a.storeid_sfa as store_id,a.link_man_sfa as link_man,a.link_phon_sfa as link_phone,a.photo as image_url,a.district,a.street,ROUND(6…...

安卓jetpack compose学习笔记-UI基础学习
哲学知识应该用哲学的方式学习,技术知识也应该用技术的方式学习。没必要用哲学的态度来学习技术。 学完安卓技术能做事就ok了,安卓技术肯定是有哲学的,但是在初学阶段没必要讨论什么安卓哲学。 学习一们复杂技术的路径有很多,这里…...
线性回归用于分类
线性回归本身是一种用于回归问题的技术,即预测一个连续的目标变量值。然而,线性回归也可以被改造或结合其他技术来用于分类问题,尽管这不是其最直接或最常见的用途。以下是几种将线性回归应用于分类问题的方法或相关概念: 阈值划分…...
解锁电商新势能:商城系统自动 SaaS 多开功能深度解析
在电商行业加速向精细化、多元化运营转型的当下,传统的商城系统部署模式已难以满足企业快速拓展业务的需求。此时,商城系统自动 SaaS 多开功能横空出世,以智能、高效、灵活的特性,成为众多电商企业突破发展瓶颈的关键利器。这一功…...

蓝桥杯_DS18B20温度传感器---新手入门级别超级详细解析
目录 一、引言 DS18B20的原理图 单总线简介: 编辑暂存器简介: DS18B20的温度转换与读取流程 二、代码配置 maic文件 疑问 关于不同格式化输出符号的使用 为什么要rd_temperature()/16.0? onewire.h文件 这个配置为什么要先读lo…...