NIO 与传统 IO:深入理解与应用场景

在 Java 编程中,IO(输入/输出)操作是不可或缺的一部分。Java 提供了两种主要的 IO 机制:传统的阻塞式 IO(Blocking IO)和非阻塞式 IO(Non-blocking IO),后者通常被称为 NIO(New IO)。本文将深入探讨这两种 IO 模型的差异,特别是在文件操作和网络传输中的应用场景。

1 传统 IO 与 NIO 的对比

1.1 传统 IO(Blocking IO)

传统 IO 基于字节流或字符流(如 FileInputStreamBufferedReader 等)进行文件读写,以及使用 SocketServerSocket 进行网络传输。传统 IO 采用阻塞式模型,对于每个连接,都需要创建一个独立的线程来处理读写操作。当一个线程在等待 I/O 操作时,无法执行其他任务,这会导致大量线程的创建和销毁,以及上下文切换,降低了系统性能。

1.2 NIO(Non-blocking IO)

NIO 使用通道(Channel)和缓冲区(Buffer)进行文件操作,以及使用 SocketChannelServerSocketChannel 进行网络传输。NIO 采用非阻塞模型,允许线程在等待 I/O 时执行其他任务。这种模式通过使用选择器(Selector)来监控多个通道(Channel)上的 I/O 事件,实现了更高的性能和可伸缩性。

2 文件操作中的 NIO 与传统 IO

2.1 性能测试

为了比较 NIO 和传统 IO 在文件操作中的性能,我们编写了一个简单的文件复制程序,分别使用传统 IO 和 NIO 进行文件复制。

public class SimpleFileTransferTest {// 使用传统的 I/O 方法传输文件private long transferFile(File source, File des) throws IOException {long startTime = System.currentTimeMillis();if (!des.exists())des.createNewFile();BufferedInputStream bis = new BufferedInputStream(new FileInputStream(source));BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(des));byte[] bytes = new byte[1024 * 1024];int len;while ((len = bis.read(bytes)) != -1) {bos.write(bytes, 0, len);}long endTime = System.currentTimeMillis();return endTime - startTime;}// 使用 NIO 方法传输文件private long transferFileWithNIO(File source, File des) throws IOException {long startTime = System.currentTimeMillis();if (!des.exists())des.createNewFile();RandomAccessFile read = new RandomAccessFile(source, "rw");RandomAccessFile write = new RandomAccessFile(des, "rw");FileChannel readChannel = read.getChannel();FileChannel writeChannel = write.getChannel();ByteBuffer byteBuffer = ByteBuffer.allocate(1024 * 1024);while (readChannel.read(byteBuffer) > 0) {byteBuffer.flip();writeChannel.write(byteBuffer);byteBuffer.clear();}writeChannel.close();readChannel.close();long endTime = System.currentTimeMillis();return endTime - startTime;}public static void main(String[] args) throws IOException {SimpleFileTransferTest simpleFileTransferTest = new SimpleFileTransferTest();File sourse = new File("[电影天堂www.dygod.cn]猜火车-cd1.rmvb");File des = new File("io.avi");File nio = new File("nio.avi");long time = simpleFileTransferTest.transferFile(sourse, des);System.out.println(time + ":普通字节流时间");long timeNio = simpleFileTransferTest.transferFileWithNIO(sourse, nio);System.out.println(timeNio + ":NIO时间");}
}

测试结果:在文件较大的情况下,传统 IO 的速度竟然比 NIO 更快。这可能是因为文件操作本身不涉及大量并发,NIO 的非阻塞特性在文件操作中并没有明显优势。

3. 网络传输中的 NIO 与传统 IO

在 Java 中,传统 IO 和 NIO 在服务器端实现上有显著的差异。传统 IO 使用阻塞式模型,而 NIO 使用非阻塞式模型,通过 Selector 实现 I/O 多路复用。下面我们将详细对比这两种模型的实现。

3.1 服务器端代码对比

传统 IO 服务器:传统 IO 服务器使用 ServerSocket 和 Socket 类来实现阻塞式 I/O。每个连接都需要一个单独的线程来处理,这在大规模并发连接的情况下会导致性能问题。

public class IOServer {public static void main(String[] args) {try {ServerSocket serverSocket = new ServerSocket(8080);while (true) {Socket client = serverSocket.accept();InputStream in = client.getInputStream();OutputStream out = client.getOutputStream();byte[] buffer = new byte[1024];int bytesRead = in.read(buffer);out.write(buffer, 0, bytesRead);in.close();out.close();client.close();}} catch (IOException e) {e.printStackTrace();}}
}

关键点:

阻塞式 I/O:serverSocket.accept() in.read(buffer) 都是阻塞的,直到有新的连接或数据到达。
线程模型:每个连接都需要一个单独的线程来处理,这在大规模并发连接的情况下会导致性能问题。

NIO 服务器:NIO 服务器使用 ServerSocketChannel 和 Selector 类来实现非阻塞式 I/O 和 I/O 多路复用。单个线程可以处理多个连接,从而提高性能。

public class NIOServer {public static void main(String[] args) {try {ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();serverSocketChannel.bind(new InetSocketAddress(8081));serverSocketChannel.configureBlocking(false);Selector selector = Selector.open();serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);while (true) {selector.select();Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();while (iterator.hasNext()) {SelectionKey key = iterator.next();iterator.remove();if (key.isAcceptable()) {ServerSocketChannel server = (ServerSocketChannel) key.channel();SocketChannel client = server.accept();client.configureBlocking(false);client.register(selector, SelectionKey.OP_READ);} else if (key.isReadable()) {SocketChannel client = (SocketChannel) key.channel();ByteBuffer buffer = ByteBuffer.allocate(1024);client.read(buffer);buffer.flip();client.write(buffer);client.close();}}}} catch (IOException e) {e.printStackTrace();}}
}

关键点:

非阻塞式 I/O:serverSocketChannel.configureBlocking(false)client.configureBlocking(false) 设置为非阻塞模式。
I/O 多路复用:使用 Selector 监控多个 SocketChannel,单个线程可以处理多个连接。
事件驱动:通过SelectionKey处理不同的事件(如OP_ACCEPTOP_READ)。

客户端测试用例:为了比较传统 IO 和 NIO 服务器的性能,我们编写了一个简单的客户端测试用例,分别测试处理 10000 个客户端请求所需的时间。

public class TestClient {public static void main(String[] args) throws InterruptedException {int clientCount = 10000;ExecutorService executorServiceIO = Executors.newFixedThreadPool(10);ExecutorService executorServiceNIO = Executors.newFixedThreadPool(10);Runnable ioClient = () -> {try {Socket socket = new Socket("localhost", 8080);OutputStream out = socket.getOutputStream();InputStream in = socket.getInputStream();out.write("Hello, 沉默王二 IO!".getBytes());byte[] buffer = new byte[1024];in.read(buffer);in.close();out.close();socket.close();} catch (IOException e) {e.printStackTrace();}};Runnable nioClient = () -> {try {SocketChannel socketChannel = SocketChannel.open();socketChannel.connect(new InetSocketAddress("localhost", 8081));ByteBuffer buffer = ByteBuffer.wrap("Hello, 沉默王二 NIO!".getBytes());socketChannel.write(buffer);buffer.clear();socketChannel.read(buffer);socketChannel.close();} catch (IOException e) {e.printStackTrace();}};long startTime, endTime;startTime = System.currentTimeMillis();for (int i = 0; i < clientCount; i++) {executorServiceIO.execute(ioClient);}executorServiceIO.shutdown();executorServiceIO.awaitTermination(1, TimeUnit.MINUTES);endTime = System.currentTimeMillis();System.out.println("传统 IO 服务器处理 " + clientCount + " 个客户端耗时: " + (endTime - startTime) + "ms");startTime = System.currentTimeMillis();for (int i = 0; i < clientCount; i++) {executorServiceNIO.execute(nioClient);}executorServiceNIO.shutdown();executorServiceNIO.awaitTermination(1, TimeUnit.MINUTES);endTime = System.currentTimeMillis();System.out.println("NIO 服务器处理 " + clientCount + " 个客户端耗时: " + (endTime - startTime) + "ms");}
}

测试结果:NIO 服务器处理 10000 个客户端请求的时间明显优于传统 IO 服务器,NIO 在网络传输中的性能优势显著。

4. 总结

  • 文件操作:传统 IO 和 NIO 在文件操作中的性能差异不大,NIO 的非阻塞特性在文件操作中没有明显优势。
  • 网络传输:NIO 在网络传输中的性能显著优于传统 IO,特别是在高并发场景下。NIO 的非阻塞模型和 I/O 多路复用机制使得单个线程可以高效地管理多个并发连接,从而提高系统性能。
  • 传统 I/O 采用阻塞式模型,线程在 I/O 操作期间无法执行其他任务。NIO 使用非阻塞模型,允许线程在等待 I/O 时执行其他任务,通过选择器(Selector)监控多个通道(Channel)上的 I/O 事件,提高性能和可伸缩性。
  • 传统 I/O 使用基于字节流或字符流的类(如 FileInputStreamBufferedReader 等)进行文件读写。NIO 使用通道(Channel)和缓冲区(Buffer)进行文件操作,NIO 在性能上的优势并不大。
  • 传统 I/O 使用 SocketServerSocket 进行网络传输,存在阻塞问题。NIO 提供了 SocketChannel ServerSocketChannel,支持非阻塞网络传输,提高了并发处理能力。

理解 NIO 和传统 IO 的差异及其适用场景,有助于在实际开发中选择合适的 IO 机制,以提高程序的性能和可扩展性。

5 思维导图

在这里插入图片描述

6 参考链接

Java NIO 比传统 IO 强在哪里?

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/475747.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

TensorFlow 2.0 windows11 GPU 训练环境配置

前言 在一切开始之前&#xff0c;请确保你的cmd命令行和powershell命令行可以正常打开。如果不能&#xff0c;建议重装系统。我不确定这是否会影响你最终的结果&#xff0c;毕竟windows的坑太多了。 安装顺序&#xff1a;visual studio -> cuda -> cudnn -> python…

使⽤MATLAB进⾏⽬标检测

目录 数据准备定义模型并训练用测试集评估性能推理过程⼀⾏代码查看⽹络结构⼀⾏代码转onnx结语 ⼈⽣苦短&#xff0c;我⽤MATLAB。 Pytorch在深度学习领域占据了半壁江⼭&#xff0c;最主要的原因是⽣态完善&#xff0c;⽽且api直观易⽤。但谁能想到现在MATLAB⽤起来⽐Pytorch…

【Java】实战:多数元素

一、题目描述 给定一个大小为 n 的数组 nums &#xff0c;返回其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素。 你可以假设数组是非空的&#xff0c;并且给定的数组总是存在多数元素。 示例 1&#xff1a; 输入&#xff1a;nums [3,2,3] 输出&#x…

JAVA:探索 PDF 文字提取的技术指南

1、简述 随着信息化的发展&#xff0c;PDF 文档成为了信息传播的重要媒介。在许多应用场景下&#xff0c;如数据迁移、内容分析和信息检索&#xff0c;我们需要从 PDF 文件中提取文字内容。JAVA提供了多种库来处理 PDF 文件&#xff0c;其中 PDFBox 和 iText 是最常用的两个。…

vue3+vant实现弹幕循环播放~

1、效果图 <!-- 弹幕 --> <div style"height: 88px"><van-barragev-model"list"duration"5000":rows"rows":gap"gap":loop"loop"style"--move-distance: -345px" ><div class&quo…

南京邮电大学算法设计-二叉树先序遍历算法动态演示

二叉树先序遍历算法动态演示 一、课题内容和要求 (1)实验目的&#xff1a; 本实验通过手动输入二叉树结点信息&#xff0c;构建相应的二叉树&#xff0c;并通过图形化界面动态演示先序遍历算法的过程。通过本次实验&#xff0c;我可以深入理解二叉树的数据结构、先序遍历算法…

大数据挖掘期末复习

大数据挖掘 数据挖掘 数据挖掘定义 技术层面&#xff1a; 数据挖掘就是从大量的、不完全的、有噪声的、模糊的、随机的实际应用数据中&#xff0c;提取隐含在其中、人们事先不知道的、但又潜在有用的信息的过程。 数据准备环节 数据选择 质量分析 数据预处理 数据仓库 …

【Anomaly Detection论文阅读记录】Resnet网络与WideResNet网络

Resnet网络 网络结构&#xff1a;(层数计算不包括max pool、average pool、softmax等操作) 层数计算&#xff08;以Resnet-18为例子&#xff09;&#xff1a; conv1conv2_xconv3_xconv4_xconv5_xfc1(22)(22)(22)(22)118 WideResNet网络 WideResNet提出了一种新的体系结构&#…

基于YOLOv8深度学习的汽车车身车损检测系统研究与实现(PyQt5界面+数据集+训练代码)

本文研究并实现了一种基于YOLOV8深度学习模型的汽车车身车损检测系统&#xff0c;旨在解决传统车损检测中效率低、精度不高的问题。该系统利用YOLOV8的目标检测能力&#xff0c;在单张图像上实现了车身损坏区域的精确识别和分类&#xff0c;尤其是在车身凹痕、车身裂纹和车身划…

【前端学习笔记】Javascript学习二(运算符、数组、函数)

一、运算符 运算符&#xff08;operator&#xff09;也被称为操作符&#xff0c;是用于实现赋值、比较和执行算数运算等功能的符号。 JavaScript中常用的运算符有&#xff1a; 算数运算符、递增和递减运算符、比较运算符、逻辑运算符、赋值运算符 算数运算符&#xff1a; 、-…

python实战案例----使用 PyQt5 构建简单的 HTTP 接口测试工具

python实战案例----使用 PyQt5 构建简单的 HTTP 接口测试工具 文章目录 python实战案例----使用 PyQt5 构建简单的 HTTP 接口测试工具项目背景技术栈用户界面核心功能实现结果展示完整代码总结 在现代软件开发中&#xff0c;测试接口的有效性与响应情况变得尤为重要。本文将指导…

网络安全之信息收集-实战-1

请注意&#xff0c;本文仅供合法和授权的渗透测试使用&#xff0c;任何未经授权的活动都是违法的。 实战&#xff1a;补天公益src“吉林通用航空职业技术学院” 奇安信&#xff5c;用户登录https://www.butian.net/Loo/submit?cid64918 域名或ip&#xff1a;https://www.jlth…

鸿蒙实战:使用隐式Want启动Ability

文章目录 1. 实战概述2. 实现步骤2.1 创建鸿蒙应用项目2.2 修改Index.ets代码2.3 创建LuzhouAbility2.4 创建Luzhou页面2.5 设置模块配置文件 3. 测试效果4. 实战总结 1. 实战概述 本次鸿蒙应用实战&#xff0c;先创建项目“ImplicitWantStartAbility”&#xff0c;接着修改In…

STM32低功耗设计NFC与无线距离感应智能钥匙扣-分享

目录 目录 前言 一、本设计主要实现哪些很“开门”功能&#xff1f; 二、电路设计原理图 1.电路图采用Altium Designer进行设计&#xff1a; 2.实物展示图片 三、程序源代码设计 四、获取资料内容 前言 智能钥匙扣作为一种小巧而实用的智能设备&#xff0c;凭借其便携性…

【Node.js】Node.js 和浏览器之间的差异

Node.js 是一个强大的运行时环境&#xff0c;它在现代 JavaScript 开发中扮演着重要角色。然而&#xff0c;许多开发者在使用 Node.js 时常常会感到困惑&#xff0c;尤其是与浏览器环境的对比。本文将深入探讨 Node.js 和浏览器之间的差异&#xff0c;帮助你全面理解两者的设计…

qt之telnet连接目标设备在线调试功能

一、前言 在QT下使用telnet连接目标设备&#xff0c;进行在线命令调试&#xff0c;也可配合ftp或ssh使用。 telnet某些库在qt5下不可用&#xff0c;无法获取登录信息&#xff0c;只能获取到连接信息&#xff0c;这里我用自己的方式判断是否成功登录 二、环境 window qt5.7…

小熊派Nano接入华为云

一、华为云IoTDA创建产品 创建如下服务&#xff0c;并添加对应的属性和命令。 二、小熊派接入 根据小熊派官方示例代码D6完成了小熊派接入华为云并实现属性上传命令下发。源码&#xff1a;小熊派开源社区/BearPi-HM_Nano 1. MQTT连接代码分析 这部分代码在oc_mqtt.c和oc_mq…

Hbuilder X/Uniapp 关于app运行调试及mumu模拟器运行问题

Hbuilder X 关于app调试问题及mumu模拟器链接问题 Hbuilder 关于app调试问题1. app运行配置2. adb路径配置3. 模拟器端口查询4. 运行 Hbuilder 关于app调试问题 1. app运行配置 Hbuilder > 工具 > 设置 > 运行配置 adb路径配置&#xff08;见2&#xff09; Android模…

MySQL-关键字执行顺序

&#x1f496;简介 在MySQL中&#xff0c;SQL查询语句的执行遵循一定的逻辑顺序&#xff0c;即使这些关键字在SQL语句中的物理排列可能有所不同。 &#x1f31f;语句顺序 (8) SELECT (9) DISTINCT<select_list> (1) FROM <left_table> (3) <join_type> JO…

【SpringBoot】26 实体映射工具(MapStruct)

Gitee 仓库 https://gitee.com/Lin_DH/system 介绍 现状 为了让应用程序的代码更易于维护&#xff0c;通常会将项目进行分层。在《阿里巴巴 Java 开发手册》中&#xff0c;推荐分层如下图所示&#xff1a; 每层都有对应的领域模型&#xff0c;即不同类型的 Bean。 DO&…