Java的IO模型详解-BIO,NIO,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.mzph.cn/pingmian/52674.shtml

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

相关文章

【读点论文】Scene Text Detection and Recognition: The Deep Learning Era

Scene Text Detection and Recognition: The Deep Learning Era Abstract 随着深度学习的兴起和发展&#xff0c;计算机视觉发生了巨大的变革和重塑。场景文本检测与识别作为计算机视觉领域的一个重要研究领域&#xff0c;不可避免地受到了这波革命的影响&#xff0c;从而进入…

Golang | Leetcode Golang题解之第376摆动序列

题目&#xff1a; 题解&#xff1a; int wiggleMaxLength(int* nums, int numsSize) {if (numsSize < 2) {return numsSize;}int prevdiff nums[1] - nums[0];int ret prevdiff ! 0 ? 2 : 1;for (int i 2; i < numsSize; i) {int diff nums[i] - nums[i - 1];if ((…

TD学习笔记————中级教程总结(NEW)

目录 Instance功能讲解 问题&#xff1a; 报错All ops must generate the same number of instances (have the same length Replicator功能讲解 问题&#xff1a; 视频分辨率过大 Cannot find function named:onValueChange Instance功能讲解 数据通道的长度要一致 N…

安泰功率放大器应用领域:MEMS传感器的应用有哪些

功率放大器的应用领域很广泛&#xff0c;从超声测试、材料测试、水声测试再到压电驱动、电磁驱动生物医疗&#xff0c;它都能为整个系统提供强劲的激励&#xff0c;同样功率放大器在MEMS传感器系统的激励中也有着良好应用&#xff0c;今天Aigtek安泰电子就带大家走进MEMS传感器…

Hadoop: Mapreduce了解

目录 1.MapReduce概述 2.MapReduce的基本工作原理 2.1.Map阶段 2.1.1.Map源码解析 2.1.2.map端代码总结 2.2.Shuffle and Sort阶段 2.3.Reduce阶段 2.3.1.Reduce源码解析 2.3.2.Reduce端源码总结 3.数据流与任务执行 3.1.数据输入与输出格式 3.1.1.TextInputFormat…

comfyui工作流,动漫转真人,效果炸裂!

在数字媒体制作领域&#xff0c;将动漫风格的图像转换为接近真人风格的视觉效果一直是一个具有挑战性的任务。最近&#xff0c;ComfyUI 推出了一套高级工具和节点系统&#xff0c;极大地简化了这一过程。本文将详细介绍这一工作流的各个组成部分以及其实用性。 工作流核心节点简…

使用腾讯云宝塔面板部署后端项目,包括MySQL,Redis,JDK,Maven

一、购买腾讯云服务器并配置 购买腾讯云的一个服务器服务后进入到如下页面&#xff0c;点击左侧栏服务器&#xff0c;然后点击“重装系统” 选择“使用应用面板”->“宝塔Linux面板”->填写自定义账号和密码->点击确认 二、配置宝塔服务器端口参数并启动 点击确认之…

2024年6月GSEP(C++)一级认证真题讲解

注意&#xff01;做题时长为2小时&#xff0c;孩子做完题目后对照讲解视频和讲解分析&#xff0c;针对薄弱点&#xff0c;进行有效的专项提高。 &#x1f451;讲解视频 &#xff08;暂无&#xff09; &#x1f451;讲解分析 1 单选题&#xff08;每题 2 分&#xff0c;共 3…

sqli-labs靶场通关攻略 46-50

主页有sqli-labs靶场通关攻略 1-45 第四六关 less-46 步骤一&#xff1a;利用报错注入查询库 ?sort1 and updatexml(1,concat(0x7e,database(),0x7e),1) 步骤二&#xff1a;查询表名 ?sort1 and updatexml(1,concat(0x7e,(select group_concat(table_name)from informatio…

WebSocket通信学习笔记

1 简介 WebSocket是一种全双工通信协议&#xff0c;它允许客户端和服务器之间建立持久化的双向连接&#xff0c;从而在不频繁创建HTTP请求的情况下进行实时数据传输。与传统的HTTP协议相比&#xff0c;WebSocket更适合需要实时数据更新的应用场景&#xff0c;如聊天应用、实时…

IDEA没有SQL语句提示

解决已经在IDEA连接数据库&#xff0c;但是写SQL语句不会提示列名、属性之类的 Mapper 映射没有 SQL 提示 设置中搜索&#xff0c;把方言改成 MySQL SQL Dialects

群晖(Docker Compose)配置 frp 服务

为了方便远程电脑&#xff0c;访问自己电脑上的ComfyUI等服务&#xff0c;配置了 frp 服务。 配置 frp 服务后&#xff0c;发现群晖中的一些服务也可以 stcp 安全的暴露出来。 直接在群晖通过 Docker Compose 方式部署 frps 和 frpc&#xff0c;访问者通过 frpc 安全访问暴露…

【机器学习】支持向量机(SVM)的对偶性、核方法以及核技巧

引言 在SVM中&#xff0c;通过引入拉格朗日乘子&#xff0c;可以将原始问题转化为对偶问题&#xff0c;这种转换具有几个重要的优点&#xff0c;包括简化计算和提供更直观的优化问题的解释 文章目录 引言一、支持向量机&#xff08;SVM&#xff09;的对偶性1.1 原始问题&#x…

pacs图像打不开怎么办 --日常工作总结

先强调一下,我不是专门做图像入库和图像归档,我负责的是临床这边的影像,下面是占在我的业务日常分析总结的哈,(不太专业,勿喷) 我们经常会遇到在打开某个检查的时候,出现黑框,日志定位wado服务取不到图 这种情况一般分为 (1) 工作站,工作组,路由,存储卷配置缺失 ---对应的wad…

如何更改 Mac 上 Java 的默认版本?

优质博文&#xff1a;IT-BLOG-CN 第一次运行/usr/libexec/java_home -V将输出类似以下内容&#xff1a; Matching Java Virtual Machines (3): 1.8.0_05, x86_64: "Java SE 8" /Library/Java/JavaVirtualMachines/jdk1.8.0_05.jdk/Contents/Home 1.6.0_65-b14-4…

安装JKS格式证书

--千金易得 知己难求 本文介绍如何在Tomcat服务器配置JKS格式的SSL证书&#xff0c;具体包括下载和上传证书文件&#xff0c;在Tomcat上配置证书文件和证书密码等参数&#xff0c;以及安装证书后结果的验证。成功配置SSL证书后&#xff0c;您将能够通过HTTPS加密通道安全访问To…

音频检测电路 | 声音传感器模块 | 口哨开关 | Arduino

音频检测电路 | 声音传感器模块 | 口哨开关 | Arduino 案例分析电路设计1. **基本音频检测电路设计**电路结构:2. **灵敏度调节原理**方法:3. **非 MCU 控制的 LED 触发**设计步骤:4. **电路示例**5. **示意图(文本描述)**总结实验方法案例分析 一个硅胶娃娃,挤压或拍打…

Docker原理及实例

目录 一 Docker简介及部署方法 1.1 Docker简介 1.1.1 什么是docker&#xff1f; 1.1.2 docker在企业中的应用场景 1.1.3 docker与虚拟化的对比 1.1.4 docker的优势 2 部署docker 2.1 容器工作方法 2.2 部署第一个容器 2.2.1 配置软件仓库 2.2.2 安装docker-ce并启动服…

【经验分享】CANOPEN协议驱动移植(基于CANfestival源码架构)

【经验分享】CANOPEN协议驱动移植(基于CANfestival源码架构&#xff09; 前言一、CANOPEN整体实现原理二、CANOPEN驱动收发三、Timer定时器四、Object Dictionary对象字典五、CANOPEN应用层接口六、CANOPEN 驱动移植经验总结 前言 本次CANOPEN移植基于CANfestival开源代码&…

SpringBoot中MyBatis使用自定义TypeHandler

&#x1f604; 19年之后由于某些原因断更了三年&#xff0c;23年重新扬帆起航&#xff0c;推出更多优质博文&#xff0c;希望大家多多支持&#xff5e; &#x1f337; 古之立大事者&#xff0c;不惟有超世之才&#xff0c;亦必有坚忍不拔之志 &#x1f390; 个人CSND主页——Mi…