Java的IO模型详解-BIO,NIO,AIO

文章目录

    • 一、BIO相关知识
      • 读写模型
      • BIO 概述
      • BIO 特点
      • BIO 实现示例
        • 服务器端
        • 客户端
    • 二、NIO相关知识点
      • 读写模型
      • NIO 核心概念
      • NIO 特点
      • NIO 实现示例
        • 服务器端
        • 客户端
    • 三、AIO相关知识
      • 读写模型
      • AIO 概念
      • AIO 组件
      • AIO 特点
      • AIO 实现示例
        • 服务器端
        • 客户端
    • 总结

一、BIO相关知识

Java 的 BIO (Blocking I/O) 模型是基于传统的同步阻塞 I/O 操作。在这种模型中,每个客户端连接都需要一个独立的线程来处理请求。当线程正在执行 I/O 操作时(如读取或写入),它会被阻塞,直到操作完成。

读写模型

在这里插入图片描述

BIO 概述

BIO 是 Java 中最简单的 I/O 模型之一,它使用 java.io 包中的类来实现,例如 InputStream, OutputStream, Reader, 和 Writer。在 BIO 模型中,每当有客户端连接到来时,服务器就会创建一个新的线程来处理这个连接上的所有读写操作。

BIO 特点

  • 同步:读写操作是同步进行的,即必须等待一个操作完成后才能继续下一步操作。
  • 阻塞:当线程调用 I/O 方法时,如果数据没有准备好,线程就会被阻塞,直到数据准备好为止。
  • 每连接一线程:对于每一个客户端连接,服务器都会分配一个线程来处理该连接的所有 I/O 操作。
  • 简单易用:BIO 的实现比较简单,易于理解和使用。
  • 低效:在高并发的情况下,由于每个连接都需要一个线程,因此线程的创建和管理成本较高,可能导致服务器资源耗尽。

BIO 实现示例

下面是一个简单的 BIO 服务器端实现示例,该服务器接收客户端的连接,并打印出客户端发送的消息。

服务器端
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;public class BioServer {public static void main(String[] args) {int port = 8080; // 服务器监听端口try (ServerSocket serverSocket = new ServerSocket(port)) {System.out.println("Server started on port " + port);while (true) {Socket clientSocket = serverSocket.accept();new Thread(new ClientHandler(clientSocket)).start();}} catch (IOException e) {e.printStackTrace();}}static class ClientHandler implements Runnable {private final Socket socket;public ClientHandler(Socket socket) {this.socket = socket;}@Overridepublic void run() {try (BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));PrintWriter out = new PrintWriter(socket.getOutputStream(), true)) {String inputLine;while ((inputLine = in.readLine()) != null) {System.out.println("Received from client: " + inputLine);if ("bye".equalsIgnoreCase(inputLine)) {break;}out.println("Echo: " + inputLine);}System.out.println("Client disconnected.");} catch (IOException e) {e.printStackTrace();}}}
}
客户端

接下来是一个简单的 BIO 客户端实现示例,该客户端向服务器发送消息并接收服务器的响应。

import java.io.*;
import java.net.Socket;public class BioClient {public static void main(String[] args) {String serverAddress = "localhost";int port = 8080;try (Socket socket = new Socket(serverAddress, port);PrintWriter out = new PrintWriter(socket.getOutputStream(), true);BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()))) {out.println("Hello, server!");out.println("Another message.");out.println("bye");String response;while ((response = in.readLine()) != null) {System.out.println("Received from server: " + response);}} catch (IOException e) {e.printStackTrace();}}
}

二、NIO相关知识点

Java 的 NIO (New I/O) 模型是在 Java 1.4 中引入的,它提供了一种新的方式来处理输入输出操作,相比于传统的 BIO (Blocking I/O) 模型,NIO 提供了更高的性能和更灵活的编程模型。NIO 主要包括三个核心组件:缓冲区(Buffer)、通道(Channel) 和选择器(Selector)。

读写模型

在这里插入图片描述

NIO 核心概念

  • 缓冲区(Buffer):缓冲区用于存储不同类型的二进制数据。与流不同的是,缓冲区可以保存数据,可以对数据进行读写操作,也可以在缓冲区之间复制数据。在 NIO 中,所有数据都是通过缓冲区进行操作的。

  • 通道(Channel):通道类似于流,但是它可以双向读写数据。通道可以从文件系统、网络等地方获取或发送数据。与流不同的是,通道可以与缓冲区交互,也就是说数据可以被读取到缓冲区中,或者从缓冲区写入到通道中。

  • 选择器(Selector):选择器允许单个线程监控多个通道的状态,比如哪些通道是可读的、哪些是可写的。这使得一个线程可以同时处理多个客户端连接,大大提高了并发处理能力。

NIO 特点

  • 非阻塞:NIO 允许非阻塞 I/O 操作,即使没有数据可用,线程也不会被阻塞,而是可以继续执行其他任务。
  • 高效:由于使用了缓冲区和选择器,NIO 可以在一个线程中处理多个连接,减少了线程的创建和销毁所带来的开销。
  • 复杂性:相比于 BIO,NIO 的编程模型更为复杂,需要更深入地理解缓冲区和通道的概念。

NIO 实现示例

下面是一个简单的 NIO 服务器端实现示例,该服务器接收客户端的连接,并打印出客户端发送的消息。

服务器端
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 NioServer {public static void main(String[] args) {int port = 8080; // 服务器监听端口try (Selector selector = Selector.open();ServerSocketChannel serverSocketChannel = ServerSocketChannel.open()) {serverSocketChannel.bind(new InetSocketAddress(port));serverSocketChannel.configureBlocking(false);serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);System.out.println("Server started on port " + port);while (true) {selector.select();Set<SelectionKey> selectedKeys = selector.selectedKeys();Iterator<SelectionKey> iterator = selectedKeys.iterator();while (iterator.hasNext()) {SelectionKey key = iterator.next();iterator.remove();if (key.isAcceptable()) {acceptConnection(serverSocketChannel, selector);} else if (key.isReadable()) {readData(key);}}}} catch (IOException e) {e.printStackTrace();}}private static void acceptConnection(ServerSocketChannel serverSocketChannel, Selector selector) throws IOException {SocketChannel clientChannel = serverSocketChannel.accept();clientChannel.configureBlocking(false);clientChannel.register(selector, SelectionKey.OP_READ);System.out.println("Client connected: " + clientChannel.getRemoteAddress());}private static void readData(SelectionKey key) throws IOException {SocketChannel clientChannel = (SocketChannel) key.channel();ByteBuffer buffer = ByteBuffer.allocate(1024);int numRead;while ((numRead = clientChannel.read(buffer)) > 0) {buffer.flip();byte[] data = new byte[numRead];buffer.get(data);String received = new String(data, "UTF-8");System.out.println("Received from client: " + received);buffer.clear();}if (numRead == -1) { // 如果客户端关闭了连接clientChannel.close();key.cancel();}}
}
客户端

下面是一个简单的 NIO 客户端实现示例,该客户端向服务器发送消息并接收服务器的响应。

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.nio.charset.StandardCharsets;public class NioClient {public static void main(String[] args) {String serverAddress = "localhost";int port = 8080;try (SocketChannel socketChannel = SocketChannel.open()) {socketChannel.connect(new InetSocketAddress(serverAddress, port));socketChannel.configureBlocking(false);ByteBuffer buffer = ByteBuffer.allocate(1024);buffer.put("Hello, server!".getBytes(StandardCharsets.UTF_8));buffer.flip();socketChannel.write(buffer);buffer.clear();buffer.put("Another message.".getBytes(StandardCharsets.UTF_8));buffer.flip();socketChannel.write(buffer);buffer.clear();buffer.put("bye".getBytes(StandardCharsets.UTF_8));buffer.flip();socketChannel.write(buffer);buffer.clear();while (socketChannel.isOpen()) {buffer.clear();int numRead = socketChannel.read(buffer);if (numRead == -1) {break;}buffer.flip();byte[] data = new byte[numRead];buffer.get(data);System.out.println("Received from server: " + new String(data, StandardCharsets.UTF_8));buffer.clear();}} catch (IOException e) {e.printStackTrace();}}
}

三、AIO相关知识

Java 的 AIO (Asynchronous I/O) 模型是在 Java 7 中引入的,它是 NIO 的扩展,支持真正的异步 I/O 操作。AIO 有时也被称作 NIO 2,因为它是 NIO 的后续版本,增强了 NIO 的功能。

读写模型

在这里插入图片描述

AIO 概念

AIO 的关键概念是异步非阻塞 I/O。在 AIO 中,应用程序发起一个 I/O 请求后,可以立即返回并继续执行其他任务,而不需要等待 I/O 操作完成。当 I/O 操作完成后,应用程序会被通知。

AIO 组件

AIO 主要涉及以下组件:

  • AsynchronousFileChannel:用于异步地读取、写入和映射文件。
  • AsynchronousSocketChannel:用于异步地处理网络连接。
  • AsynchronousServerSocketChannel:用于异步地接受新的连接。
  • CompletionHandler:用于处理异步 I/O 操作完成时的回调。

AIO 特点

  • 异步性:AIO 支持真正的异步 I/O 操作,这意味着线程可以在发起 I/O 操作后立即返回,而不是等待操作完成。
  • 非阻塞:线程不会因为等待 I/O 操作而被阻塞,可以继续执行其他任务。
  • 高并发:AIO 适合处理高并发的场景,因为它可以有效地利用少量线程处理大量的 I/O 操作。
  • 复杂性:相比 BIO 和 NIO,AIO 的编程模型更为复杂,需要熟悉异步编程的概念。

AIO 实现示例

下面是一个简单的 AIO 服务器端实现示例,该服务器接收客户端的连接,并打印出客户端发送的消息。

服务器端
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;public class AioServer {public static void main(String[] args) {int port = 8080; // 服务器监听端口try (AsynchronousServerSocketChannel serverChannel = AsynchronousServerSocketChannel.open().bind(new InetSocketAddress(port))) {System.out.println("Server started on port " + port);serverChannel.accept(null, new CompletionHandler<AsynchronousSocketChannel, Void>() {@Overridepublic void completed(AsynchronousSocketChannel result, Void attachment) {serverChannel.accept(null, this);ByteBuffer buffer = ByteBuffer.allocate(1024);result.read(buffer, buffer, new CompletionHandler<Integer, ByteBuffer>() {@Overridepublic void completed(Integer result, ByteBuffer attachment) {attachment.flip();byte[] data = new byte[attachment.remaining()];attachment.get(data);System.out.println("Received from client: " + new String(data));result.write(ByteBuffer.wrap("Echo: ".getBytes()), ByteBuffer.wrap(data), new CompletionHandler<Integer, ByteBuffer>() {@Overridepublic void completed(Integer result, ByteBuffer attachment) {attachment.flip();result.read(attachment, attachment, this);}@Overridepublic void failed(Throwable exc, ByteBuffer attachment) {exc.printStackTrace();}});}@Overridepublic void failed(Throwable exc, ByteBuffer attachment) {exc.printStackTrace();}});}@Overridepublic void failed(Throwable exc, Void attachment) {exc.printStackTrace();}});while (true) {Thread.sleep(1000); // 让服务器运行}} catch (IOException | InterruptedException e) {e.printStackTrace();}}
}
客户端

下面是一个简单的 AIO 客户端实现示例,该客户端向服务器发送消息并接收服务器的响应。

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;public class AioClient {public static void main(String[] args) {String serverAddress = "localhost";int port = 8080;try (AsynchronousSocketChannel socketChannel = AsynchronousSocketChannel.open()) {socketChannel.connect(new InetSocketAddress(serverAddress, port), null, new CompletionHandler<Void, Void>() {@Overridepublic void completed(Void result, Void attachment) {ByteBuffer buffer = ByteBuffer.allocate(1024);buffer.put("Hello, server!".getBytes());buffer.flip();socketChannel.write(buffer, buffer, new CompletionHandler<Integer, ByteBuffer>() {@Overridepublic void completed(Integer result, ByteBuffer attachment) {attachment.clear();buffer.put("Another message.".getBytes());buffer.flip();socketChannel.write(buffer, buffer, new CompletionHandler<Integer, ByteBuffer>() {@Overridepublic void completed(Integer result, ByteBuffer attachment) {attachment.clear();buffer.put("bye".getBytes());buffer.flip();socketChannel.write(buffer, buffer, new CompletionHandler<Integer, ByteBuffer>() {@Overridepublic void completed(Integer result, ByteBuffer attachment) {buffer.clear();socketChannel.read(buffer, buffer, new CompletionHandler<Integer, ByteBuffer>() {@Overridepublic void completed(Integer result, ByteBuffer attachment) {attachment.flip();byte[] data = new byte[attachment.remaining()];attachment.get(data);System.out.println("Received from server: " + new String(data));socketChannel.close();}@Overridepublic void failed(Throwable exc, ByteBuffer attachment) {exc.printStackTrace();}});}@Overridepublic void failed(Throwable exc, ByteBuffer attachment) {exc.printStackTrace();}});}@Overridepublic void failed(Throwable exc, ByteBuffer attachment) {exc.printStackTrace();}});}@Overridepublic void failed(Throwable exc, ByteBuffer attachment) {exc.printStackTrace();}});}@Overridepublic void failed(Throwable exc, Void attachment) {exc.printStackTrace();}});while (true) {Thread.sleep(1000); // 让客户端运行}} catch (IOException | InterruptedException e) {e.printStackTrace();}}
}

总结

为了便于类比,所以样例代码都是使用了客户端和服务端的交互代码实现。通过比对不难发现一下特点:

  • BIO:简单,易于理解,但在高并发情况下效率较低。
  1. 发起读请求
  2. 等待数据(阻塞)
  3. 返回(数据已经准备好了)
  • NIO:提高了效率,支持非阻塞 I/O,但编程复杂度增加。
  1. 发起读请求
  2. 请求返回,可以继续做自己的事情
  3. 问一下数据好了没?
  4. 没好, 直接返回
  5. 哈了, 等待数据拷贝好, 返回
  • AIO:进一步提高了效率,支持真正的异步 I/O,但编程复杂度最高。
  1. 发起读请求
  2. 请求返回,做自己的事情
  3. 数据准好了, 提醒请求方数据可以用了
  • 其他说明
  1. 实际编程中, 使用较多的是bio, 因为其代码简单,易于理解。
  2. nio则一般用于框架核心级别的代码, 编程难度介于BIO和AIO之间, 且有如netty之类的较为成熟的框架面世
  3. aio 是三种IO当中效率最高的, 但是因其编程难度极高, 目前的使用案例较少。而一般开发过程更是很少用到aio.

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

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

相关文章

从0开始学杂项 第八期:流量分析(2) 数据提取

Misc 学习&#xff08;八&#xff09; - 流量分析&#xff1a;数据提取 这一期&#xff0c;我们主要写一下如何进行比较繁多的数据的提取。 使用 Tshark 批量提取数据 有时候&#xff0c;我们会需要从多个包中提取数据&#xff0c;然后再进行截取和组合&#xff0c;比如分析…

人机环境系统智能与Petri网

人机环境系统工程是一门新兴的交叉学科&#xff0c;它以人、机、环境为系统&#xff0c;研究系统整体的优化。而 Petri 网是一种用于描述和分析系统动态行为的图形化建模工具。 在人机环境系统中&#xff0c;智能体现在人、机、环境三个要素之间的相互作用和协同工作。人的智能…

嵌入式24千兆电口+4万兆光口管理型三层交换机RTL9301模块

核心模块概述: 嵌入式RTL9301模块可以支持4口万兆上联24口千兆三层管理型以太网交换机&#xff0c;也就是最多可以提供24个10/100/1000自适应电口、4个10 Gb SFP 端口、1个console口、1个USB串口。 完善的安全控制策略及CPU保护策略(CPU protect policy)提高容错能力&#xff0…

振动分析-25-频域分析之深入理解包络分析的计算过程

1 拍和幅值调制的区别 1.1 拍的现象 当两个幅值和频率相近的简谐波进行叠加时,会出现幅值忽高忽低的现象,也就是所谓的“拍”现象,但它又不同于幅值调制,虽然在时域上表现相同:都是幅值忽高忽低,但二者有着本质的区别。 当同方向的两个频率相差不大的简谐波叠加时,叠…

QT 信号和槽

效果 代码 在窗体的头文件中定义信号函数&#xff0c;注意只定义不实现 信号的返回值类型都是 void 后面是函数名() 槽函数可以有参数。一定要实现 //信号和槽函数绑定 connect(ui->btnSignalsSlots,SIGNAL(clicked()),this, SLOT(ViewSlot())); connect()函数是一个…

【unity实战】利用Root Motion+Blend Tree+Input System+Cinemachine制作一个简单的角色控制器

文章目录 前言动画设置Blend Tree配置角色添加刚体和碰撞体代码控制人物移动那么我们接下来调整一下相机的视角效果参考完结 前言 Input System知识参考&#xff1a; 【推荐100个unity插件之18】Unity 新版输入系统Input System的使用&#xff0c;看这篇就够了 Cinemachine虚…

Burp Suite Professional 2024.8 for macOS x64 ARM64 - 领先的 Web 渗透测试软件

Burp Suite Professional 2024.8 for macOS x64 & ARM64 - 领先的 Web 渗透测试软件 世界排名第一的 Web 渗透测试工具包 请访问原文链接&#xff1a;https://sysin.org/blog/burp-suite-pro-mac/&#xff0c;查看最新版。原创作品&#xff0c;转载请保留出处。 作者主页…

vivado 创建时间约束1

步骤3&#xff1a;创建时间约束 在此步骤中&#xff0c;您打开合成的设计并使用AMD Vivado™定时约束 男巫定时约束向导分析门级网表并发现缺失 约束。使用“定时约束”向导为此设计生成约束。 1.在“流导航器”中&#xff0c;单击“打开综合设计”。 2.当综合设计打开时&#…

Java | Leetcode Java题解之第385题迷你语法分析器

题目&#xff1a; 题解&#xff1a; class Solution {public NestedInteger deserialize(String s) {if (s.charAt(0) ! [) {return new NestedInteger(Integer.parseInt(s));}Deque<NestedInteger> stack new ArrayDeque<NestedInteger>();int num 0;boolean n…

微链接: 利用 MinIO 实现计算和存储的还原

Microblink 是一家专门从事图像检测的 AI 公司。他们从 BlinkID、BlinkID Verify 和 BlinkCard 等产品开始进入身份空间。最近&#xff0c;他们的图像检测能力催生了可以处理其他类型图像的产品。例如&#xff0c;可以对收据执行产品检测&#xff0c;从而使用收据上的产品描述来…

【图解版】Likes Vs Dislikes——C语言提高题【7 kyu】

一、原题 链接&#xff1a;Training on Likes Vs Dislikes | Codewars YouTube had a like and a dislike button, which allowed users to express their opinions about particular content. It was set up in such a way that you cannot like and dislike a video at the…

云原生 | 在 Kubernetes 中使用 Cilium 替代 Calico 网络插件实践指南!

[ 知识是人生的灯塔,只有不断学习,才能照亮前行的道路 ] 0x00 简述介绍 什么是 Cilium? Cilium 是一款开源软件,它基于一种名为eBPF的新的Linux内核技术提供动力,用于透明地保护使用 Docker 和 Kubernetes 等Linux 容器管理平台中部署的应用程序服务之间的网络连接,Ciliu…

kubernetes中的ParallelizeUntil()框架源码解读与使用

概述 摘要&#xff1a;本文从源码层面解读了kubernetes源码中常用的workqueue.ParallelizeParallelizeUntil()框架的源码实现&#xff0c;并且本文也将举例说明了workqueue.ParallelizeUntil()方法的典型使用场景。 正文 说明&#xff1a;基于 kubernetes v1.18.0 源码分析 …

【Qt 即时通讯系统】信息消息核心类的编写

文章目录 1. 获得唯一的 messageId2. 转成格式化时间3. 把QByteArray数据转成QIcon 1. 获得唯一的 messageId &#x1f427;通过createUuid()可以获得全球唯一的身份标识&#xff0c;Qt中对UUID是有封装的&#xff0c;获取的结果其实是一串十六进制数。 2. 转成格式化时间 …

数字化转型的内容框架解析,附华为数字化转型内容框架及方法论

数字化转型的内容框架是一个系统性、多维度的体系&#xff0c;旨在通过数字技术的融入和应用&#xff0c;对传统业务、流程和模式进行重构、升级&#xff0c;以提升效率、创造更多价值。以下是对数字化转型内容框架的详细阐述&#xff1a; 一、总体要求 数字化转型的总体要求…

Open3D 体素随机下采样

目录 一、概述 1.1原理 1.2实现步骤 1.3应用场景 二、代码实现 三、实现效果 3.1原始点云 3.2体素下采样后点云 Open3D点云算法汇总及实战案例汇总的目录地址&#xff1a; Open3D点云算法与点云深度学习案例汇总&#xff08;长期更新&#xff09;-CSDN博客 一、概述 体…

AD19基础应用技巧:捕捉对象功能的讲解鼠标”绿色十字”大光标、小光标切换

AD PCB 中心点捕捉功能&#xff1a; 线段、圆、边框中心点捕捉。 有时候不想要鼠标自动捕捉中心点怎么办&#xff1f; 关于Altium Designer 20 的捕抓功能的讲解&#xff08;https://blog.csdn.net/weixin_44599693/article/details/126177841&#xff09; ——- AD PCB画板…

详解si5338 si53xx 设计使用及STM32 iic驱动设计

背景 在实际项目中经常使用si5338 si53xx&#xff0c;进行多路时钟的倍频以生成想要的时钟信号&#xff0c;但是针对si5338 si53xx设计使用缺少相关的资料&#xff0c;本文详解si5338 si53xx 设计使用及STM32 iic驱动设计&#xff0c;本文使用工程在项目中得到测试&#xff0c…

虚拟系统VS

定义 虚拟系统VS&#xff08;Virtual System&#xff09;是指将一台物理设备PS&#xff08;Physical System&#xff09;虚拟成多个相互隔离的逻辑系统。每个VS独立工作&#xff0c;在业务功能上等同于一台独立的传统物理设备&#xff0c;如图2-1所示。 目的 随着网络规模的不…

省钱的开源项目「GitHub 热点速览」

本期&#xff0c;我从上周的热门开源项目中挑选了 5 个既省钱又省事&#xff0c;还好玩的开源项目。 首先&#xff0c;推荐的是省钱的电动汽车智能充电管理平台 evcc&#xff0c;它可以根据分时电价智能安排电动车充电时间&#xff0c;从而降低电费&#xff0c;如果你家还有太阳…