详解Java中的BIO、NIO、AIO
1、 详解Java中的BIO、AIO、NIO
1.1、引言
IO流是Java中比较难理解的一个知识点,但是IO流在实际的开发场景中经常会使用到,比如Dubbo底层就是NIO进行通讯。本文将介绍Java发展过程中出现的三种IO:BIO、NIO以及AIO,重点介绍NIO。
1.2、什么是BIO
BIO即同步阻塞IO,实现模型为一个连接就需要一个线程去处理。这种方式简单来说就是当有客户端来请求服务器时,服务器就会开启一个线程去处理这个请求,即使这个请求不干任何事情,这个线程都一直处于阻塞状态。
BIO模型有很多缺点,最大的缺点就是资源的浪费。想象一下如果QQ使用BIO模型,当有一个人上线时就需要一个线程,即使这个人不聊天,这个线程也一直被占用,那再多的服务器资源都不管用。
代码演示:package com.atguigu.bio;import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class BIOServer {public static void main(String[] args) throws Exception {//线程池机制//思路//1. 创建一个线程池//2. 如果有客户端连接,就创建一个线程,与之通讯(单独写一个方法)ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();//创建ServerSocketServerSocket serverSocket = new ServerSocket(6666);System.out.println("服务器启动了");while (true) {System.out.println("线程信息 id =" + Thread.currentThread().getId() + " 名字=" + Thread.currentThread().getName());//监听,等待客户端连接System.out.println("等待连接....");final Socket socket = serverSocket.accept();System.out.println("连接到一个客户端");//就创建一个线程,与之通讯(单独写一个方法)newCachedThreadPool.execute(new Runnable() {public void run() { //我们重写//可以和客户端通讯handler(socket);}});}}//编写一个handler方法,和客户端通讯public static void handler(Socket socket) {try {System.out.println("线程信息 id =" + Thread.currentThread().getId() + " 名字=" + Thread.currentThread().getName());byte[] bytes = new byte[1024];//通过socket 获取输入流InputStream inputStream = socket.getInputStream();//循环的读取客户端发送的数据while (true) {System.out.println("线程信息 id =" + Thread.currentThread().getId() + " 名字=" + Thread.currentThread().getName());System.out.println("read....");int read = inputStream.read(bytes);if(read != -1) {System.out.println(new String(bytes, 0, read)); //输出客户端发送的数据} else {break;}}}catch (Exception e) {e.printStackTrace();}finally {System.out.println("关闭和client的连接");try {socket.close();}catch (Exception e) {e.printStackTrace();}}}
}
演示
快捷键win+R打开cmd窗口,根据上面代码拼写6666端口
输入 telnet 127.0.0.1 6666
进入黑窗口
输入ctrl+]
向服务端发送文本
send hello world
服务端接收的
当有
多个cmd窗口时,则创建多一个线程与之对应
退出时,cmd输入
quit
服务端显示
2、什么是NIO
BIO是阻塞的,如果没有多线程,BIO就需要一直占用CPU,而NIO则是非阻塞IO,NIO在获取连接或者请求时,即使没有取得连接和数据,也不会阻塞程序。NIO的服务器实现模式为一个线程可以处理多个请求(连接)。
NIO有几个知识点需要掌握,Channel(通道),Buffer(缓冲区), Selector(多路复用选择器)。
Channel既可以用来进行读操作,又可以用来进行写操作。NIO中常用的Channel有FileChannel
、SocketChannel、ServerSocketChannel、DatagramChannel。
Buffer缓冲区用来发送和接受数据。
Selector 一般称为选择器或者多路复用器 。它是Java NIO核心组件中的一个,用于检查一个或多个NIO Channel(通道)的状态是否处于可读、可写。在javaNIO中使用Selector往往是将Channel注册到Selector中。
下面我通过代码的方式模拟javaNIO的运行流程。
2.1、NIO代码实践
首先贴上NIO的实践代码:NIO服务端详细的执行过程是这样的:创建一个ServerSocketChannel和Selector,然后将ServerSocketChannel注册到Selector上
Selector通过select方法去轮询监听channel事件,如果有客户端要连接时,监听到连接事件。
通过channel方法将socketchannel绑定到ServerSocketChannel上,绑定通过SelectorKey实现。
socketchannel注册到Selector上,关心读事件。
Selector通过select方法去轮询监听channel事件,当监听到有读事件时,ServerSocketChannel通过绑定的SelectorKey定位到具体的channel,读取里面的数据。
public class NioServer {public static void main(String[] args) throws IOException {//创建一个socket通道,并且设置为非阻塞的方式ServerSocketChannel serverSocketChannel=ServerSocketChannel.open();serverSocketChannel.configureBlocking(false);serverSocketChannel.socket().bind(new InetSocketAddress(9000));//创建一个selector选择器,把channel注册到selector选择器上Selector selector=Selector.open();serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);while (true){System.out.println("等待事件发生");selector.select();System.out.println("有事件发生了");Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();while (iterator.hasNext()){SelectionKey key = iterator.next();iterator.remove();handle(key);}}}private static void handle(SelectionKey key) throws IOException {if (key.isAcceptable()){System.out.println("连接事件发生");ServerSocketChannel serverSocketChannel= (ServerSocketChannel) key.channel();//创建客户端一侧的channel,并注册到selector上SocketChannel socketChannel = serverSocketChannel.accept();socketChannel.configureBlocking(false);socketChannel.register(key.selector(),SelectionKey.OP_READ);}else if (key.isReadable()){System.out.println("数据可读事件发生");SocketChannel socketChannel= (SocketChannel) key.channel();ByteBuffer buffer = ByteBuffer.allocate(1024);int len = socketChannel.read(buffer);if (len!=-1){System.out.println("读取到客户端发送的数据:"+new String(buffer.array(),0,len));}//给客户端发送信息ByteBuffer wrap = ByteBuffer.wrap("hello world".getBytes());socketChannel.write(wrap);key.interestOps(SelectionKey.OP_READ|SelectionKey.OP_WRITE);socketChannel.close();}}
}
客户端代码:NIO客户端代码的实现比BIO复杂很多,主要的区别在于,NIO的客户端也需要去轮询自己和服务端的连接情况。

客户端发送数据后,获取到了来自服务端的回复。
![]()
2.2、NIO总结
NIO通过一个Selector,负责监听各种IO事件的发生,然后交给后端的线程去处理。NIO相比与BIO而言,非阻塞体现在轮询处理上。BIO后端线程需要阻塞等待客户端写数据,如果客户端不写数据就一直处于阻塞状态。而NIO通过Selector进行轮询已注册的客户端,当有事件发生时才会交给后端去处理,后端线程不需要等待。
3、什么是AIO
AIO是在JDK1.7中推出的新的IO方式–异步非阻塞IO,也被称为NIO2.0,AIO在进行读写操作时,直接调用API的read和write方法即可,这两种均是异步的方法,且完成后会主动调用回调函数。简单来讲,当有流可读取时,操作系统会将可读的流传入read方法的缓冲区,并通知应用程序;对于写操作而言,当操作系统将write方法传递的流写入完毕时,操作系统主动通知应用程序。
Java提供了四个异步通道:AsynchronousSocketChannel、AsynchronousServerSocketChannel、AsynchronousFileChannel、AsynchronousDatagramChannel。
3.1、AIO代码实践
服务器端代码:AIO的创建方式和NIO类似,先创建通道,再绑定,再监听。只不过AIO中使用了异步的通道。
public class AIOServer {public static void main(String[] args) {try {//创建异步通道AsynchronousServerSocketChannel serverSocketChannel=AsynchronousServerSocketChannel.open();serverSocketChannel.bind(new InetSocketAddress(8080));System.out.println("等待连接中");//在AIO中,accept有两个参数,// 第一个参数是一个泛型,可以用来控制想传递的对象// 第二个参数CompletionHandler,用来处理监听成功和失败的逻辑// 如此设置监听的原因是因为这里的监听是一个类似于递归的操作,每次监听成功后要开启下一个监听serverSocketChannel.accept(null, new CompletionHandler<AsynchronousSocketChannel, Object>() {//请求成功处理逻辑@Overridepublic void completed(AsynchronousSocketChannel result, Object attachment) {System.out.println("连接成功,处理数据中");//开启新的监听serverSocketChannel.accept(null,this);handledata(result);}@Overridepublic void failed(Throwable exc, Object attachment) {System.out.println("失败");}});try {TimeUnit.SECONDS.sleep(Integer.MAX_VALUE);} catch (InterruptedException e) {e.printStackTrace();}} catch (IOException e) {e.printStackTrace();}}private static void handledata(AsynchronousSocketChannel result) {ByteBuffer byteBuffer=ByteBuffer.allocate(1024);//通道的read方法也带有三个参数//1.目的地:处理客户端传递数据的中转缓存,可以不使用//2.处理客户端传递数据的对象//3.处理逻辑,也有成功和不成功的两个写法result.read(byteBuffer, byteBuffer, new CompletionHandler<Integer, ByteBuffer>() {@Overridepublic void completed(Integer result, ByteBuffer attachment) {if (result>0){attachment.flip();byte[] array = attachment.array();System.out.println(new String(array));}}@Overridepublic void failed(Throwable exc, ByteBuffer attachment) {System.out.println("失败");}});}
}
客户端代码基本上没有太多差别,主要还是实现数据的发送功能
public class AIOClient {public static void main(String[] args) {try {AsynchronousSocketChannel socketChannel=AsynchronousSocketChannel.open();socketChannel.connect(new InetSocketAddress("127.0.0.1",8080));Scanner scanner=new Scanner(System.in);String next = scanner.next();ByteBuffer byteBuffer=ByteBuffer.allocate(1024);byteBuffer.put(next.getBytes());byteBuffer.flip();socketChannel.write(byteBuffer);} catch (IOException e) {e.printStackTrace();}}
}
观察结果:

相关文章:
详解Java中的BIO、NIO、AIO
1、 详解Java中的BIO、AIO、NIO 1.1、引言 IO流是Java中比较难理解的一个知识点,但是IO流在实际的开发场景中经常会使用到,比如Dubbo底层就是NIO进行通讯。本文将介绍Java发展过程中出现的三种IO:BIO、NIO以及AIO,重点介绍NIO。…...
CAN和CANFD如何转换和通信
随着科技的发展,汽车电子和工业领域中CAN通信需要承载数据量也越来越大,传统CAN通信有了向CANFD通信过渡的倾向。在实现过渡的过程中可能会出现自己设备是CAN通信,客户设备是CANFD通信的情况,或者自己设备是CANFD通信,…...
QDateTimeEdit Class
Header:#include qmake:QT += widgets Inherits:QAbstractSpinBox Inherited By:QDateEdit and QTimeEdit Public Types enum Section {NoSection, AmPmSection, MSecSection, SecondSection, MinuteSection, …, YearSection } flags SectionsProperties calendarPopu…...
Windows环境安装CentOS7
【注意】安装CentOS需要先安装Vmware虚拟机 【下载前准备】 一、下载CentOS 7镜像文件阿里云镜像开源,点击跳转 二、安装VMware(17): a. 官网,点击跳转 b. 许可证:JU090-6039P-08409-8J0QH-2YR7F 安装V…...
用docker启动mysql步骤
以下是在 Docker 中启动 MySQL 的详细步骤: **一、拉取 MySQL 镜像 ** 1. 打开终端,确保 Docker 服务正在运行。可以使用以下命令检查 Docker 服务状态: sudo systemctl status docker 2. 使用以下命令拉取 MySQL 官方镜像: d…...
[Linux] Linux 初识进程地址空间 (进程地址空间第一弹)
标题:[Linux] Linux初识进程地址空间 个人主页水墨不写bug (图片来源于AI) 目录 一、什么是进程地址空间 二、为什么父子进程相同地址的变量的值不同 三、初识虚拟地址、页表 一、什么是进程地址空间 其实,在很久之前…...
力扣21~25题
21题(简单): 分析: 按要求照做就好了,这种链表基本操作适合用c写,python用起来真的很奇怪 python代码: # Definition for singly-linked list. # class ListNode: # def __init__(self, v…...
04. prometheus 监控 Windows 服务器
prometheus 监控 Windows 服务器 1. 下载安装 Windows_exporter 安装包下载:https://github.com/prometheus-community/windows_exporter/releases 下载 msi 版本,上传至要监控的 Windows 服务器,双击安装即可,exporter 会自动…...
【机器学习】——决策树以及随机森林
文章目录 1. 决策树的基本概念与结构1.1 决策树的构建过程 2. 决策树的划分标准2.1 信息增益(Information Gain)2.2 信息增益比(Information Gain Ratio)2.3 基尼指数(Gini Index)2.4 均方误差(…...
怎么选择合适的数据恢复软件?适用于 Windows 的数据恢复软件对比
针对 Windows 的领先数据恢复软件的全面回顾: 丢失重要数据对任何 Windows 用户来说都是一场噩梦。从意外删除到系统崩溃,数据丢失是一个非常普遍的问题。值得庆幸的是,有强大的数据恢复工具可以帮助找回丢失的文件。这篇评论深入探讨了适用于…...
CI/CD 和 DevOps 工具概述:Jenkins 、Docker 的概述、工作流程、对比
随着软件开发的复杂性不断增加,持续集成(CI)、持续交付(CD)和运维(Ops)的概念逐渐成为现代软件开发流程中的核心组成部分。这些概念促进了开发团队与运维团队之间的协作,提升了软件的…...
基于SpringBoot+Vue+uniapp的高校教务管理小程序系统设计和实现
2. 详细视频演示 文章底部名片,联系我获取更详细的演示视频 3. 论文参考 4. 项目运行截图 代码运行,效果展示图 代码运行,效果展示图 代码运行,效果展示图 代码运行,效果展示图 代码运行,效果展示图 5. 技…...
如何在 Ubuntu VPS 上从 Apache Web 服务器迁移到 Nginx
前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。 简介 在启动网站或应用程序时,您需要做出许多选择。有时,您的需求会发生变化,新技术变得可行&#x…...
pikachu靶场总结(一)
最近看到好多人还在打这个靶机所以把以前写的总结放上来了,主要是皮卡丘靶场漏洞的原理,利用方式,防护方法简略总结,纯手敲记录,总结如果不到位请评论区留言! 一、暴力破解 1.基于表单的暴力破解 原理&a…...
No.2 笔记 | 网络安全攻防:PC、CS工具与移动应用分析
引言 在当今数字化时代,网络安全已成为每个人都应该关注的重要话题。本文将总结一次关于网络安全攻防技术的学习内容,涵盖PC端和移动端的恶意程序利用,以及强大的渗透测试工具Cobalt Strike的使用。通过学习这些内容,我们不仅能够了解攻击者的手法,更能提高自身的安全意识和防…...
QD1-P8 HTML格式化标签
本节学习:HTML 格式化标签。 本节视频 www.bilibili.com/video/BV1n64y1U7oj?p8 一、font 标签 用途:定义文本的字体大小、颜色和 face(字体类型)。 示例 <!DOCTYPE html> <html><head><meta cha…...
WordPress修改固定链接后301的重定向方法
网站改版实际上是很忌讳的,尤其是针对已被搜索引擎收录的网站,新站不用考虑这些问题,而已经收录的网站网页在不遵守搜索引擎规则的前提下,是会被降权,关键词排名下滑、流量IP会被剥夺、收录会减少 、业务成交量会急剧下…...
关于Allegro导出Gerber时的槽孔问题
注意点一: 如果设计的板子中有 槽孔和通孔(俗称圆孔),不仅要NC Drill, 还要 NC Route allegro导出的槽孔文件后缀是 .rou 圆型孔后缀 是 .drl ,出gerber时需要看下是否有该文件。 注意点二: 导出钻孔文件时,设置参…...
平时使用的正则总结
1、将某一个字符串的后缀名后面加上“!400_500” 使用场景是将minio拿过来的图片压缩尺寸从而压缩其大小,加快渲染的速度。需要在图片的后缀名后面加上尺寸如下: const str //storage-test.test.shiqiao.com/gateway/common/isopen/2024/10/09/e708e9…...
[万字解析]从零开始使用transformers微调huggingface格式的中文Bert模型的过程以及可能出现的问题
系列文章目录 使用transformers中的pipeline调用huggingface中模型过程中可能遇到的问题和修改建议 [万字解析]从零开始使用transformers微调huggingface格式的中文Bert模型的过程以及可能出现的问题 文章目录 系列文章目录前言模型与数据集下载模型下载数据集下载 数据加载、…...
中南大学无人机智能体的全面评估!BEDI:用于评估无人机上具身智能体的综合性基准测试
作者:Mingning Guo, Mengwei Wu, Jiarun He, Shaoxian Li, Haifeng Li, Chao Tao单位:中南大学地球科学与信息物理学院论文标题:BEDI: A Comprehensive Benchmark for Evaluating Embodied Agents on UAVs论文链接:https://arxiv.…...
UDP(Echoserver)
网络命令 Ping 命令 检测网络是否连通 使用方法: ping -c 次数 网址ping -c 3 www.baidu.comnetstat 命令 netstat 是一个用来查看网络状态的重要工具. 语法:netstat [选项] 功能:查看网络状态 常用选项: n 拒绝显示别名&#…...
04-初识css
一、css样式引入 1.1.内部样式 <div style"width: 100px;"></div>1.2.外部样式 1.2.1.外部样式1 <style>.aa {width: 100px;} </style> <div class"aa"></div>1.2.2.外部样式2 <!-- rel内表面引入的是style样…...
排序算法总结(C++)
目录 一、稳定性二、排序算法选择、冒泡、插入排序归并排序随机快速排序堆排序基数排序计数排序 三、总结 一、稳定性 排序算法的稳定性是指:同样大小的样本 **(同样大小的数据)**在排序之后不会改变原始的相对次序。 稳定性对基础类型对象…...
【 java 虚拟机知识 第一篇 】
目录 1.内存模型 1.1.JVM内存模型的介绍 1.2.堆和栈的区别 1.3.栈的存储细节 1.4.堆的部分 1.5.程序计数器的作用 1.6.方法区的内容 1.7.字符串池 1.8.引用类型 1.9.内存泄漏与内存溢出 1.10.会出现内存溢出的结构 1.内存模型 1.1.JVM内存模型的介绍 内存模型主要分…...
华为OD最新机试真题-数组组成的最小数字-OD统一考试(B卷)
题目描述 给定一个整型数组,请从该数组中选择3个元素 组成最小数字并输出 (如果数组长度小于3,则选择数组中所有元素来组成最小数字)。 输入描述 行用半角逗号分割的字符串记录的整型数组,0<数组长度<= 100,0<整数的取值范围<= 10000。 输出描述 由3个元素组成…...
如何通过git命令查看项目连接的仓库地址?
要通过 Git 命令查看项目连接的仓库地址,您可以使用以下几种方法: 1. 查看所有远程仓库地址 使用 git remote -v 命令,它会显示项目中配置的所有远程仓库及其对应的 URL: git remote -v输出示例: origin https://…...
内窥镜检查中基于提示的息肉分割|文献速递-深度学习医疗AI最新文献
Title 题目 Prompt-based polyp segmentation during endoscopy 内窥镜检查中基于提示的息肉分割 01 文献速递介绍 以下是对这段英文内容的中文翻译: ### 胃肠道癌症的发病率呈上升趋势,且有年轻化倾向(Bray等人,2018&#x…...
shell脚本质数判断
shell脚本质数判断 shell输入一个正整数,判断是否为质数(素数)shell求1-100内的质数shell求给定数组输出其中的质数 shell输入一个正整数,判断是否为质数(素数) 思路: 1:1 2:1 2 3:1 2 3 4:1 2 3 4 5:1 2 3 4 5-------> 3:2 4:2 3 5:2 3…...
JavaScript 标签加载
目录 JavaScript 标签加载script 标签的 async 和 defer 属性,分别代表什么,有什么区别1. 普通 script 标签2. async 属性3. defer 属性4. type"module"5. 各种加载方式的对比6. 使用建议 JavaScript 标签加载 script 标签的 async 和 defer …...
