代码分析Java中的BIO与NIO
开发环境
OS:Win10(需要开启telnet服务,或使用第三方远程工具)
Java版本:8
BIO
概念
BIO(Block IO),即同步阻塞IO,特点为当客户端发起请求后,在服务端未处理完该请求之前,客户端将一直等待服务端的响应。而服务端在此时也专注于该请求的处理,无法处理其它客户端的请求。
示例
package com.mlyzr.bio;import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.util.Calendar;/*** @author 勿忘初心* @since 2023-08-07-23:59* 同步阻塞IO示例*/
public class BioServer {public static void main(String[] args) throws IOException {// 监听本地8096端口ServerSocket serverSocket = new ServerSocket(8096);while(true){System.out.println("等待客户端连接...");// 接收客户端请求,阻塞Socket clientSocket = serverSocket.accept();System.out.println("监听到客户端连接");// 使用NIO处理请求信息handler(clientSocket);}}public static void handler(Socket clientSocket) throws IOException {byte[] bytes = new byte[1024];System.out.println("准备读取");int read = clientSocket.getInputStream().read(bytes);System.out.println("读取完毕");if(read != -1){System.out.println("接收到来自客户端的数据 "+new String(bytes,0,read,StandardCharsets.UTF_8));}}
}
在IDEA运行上述代码后,将在控制台看如下输出

此时先打开第一个客户端,这里使用telnet连接,具体操作为使用快捷键 Win+R 打开命令行,输入cmd,输入telnet localhost 8096(代码中绑定的端口)后回车,按下 ctrl+] (ctrl+ 右括号)即可进入telnet的交互模式,可向服务端发送信息。(telnet命令报错请使用自行搜索开启telnet服务,或使用其它工具如mobxterm等)
注意:cmd工具的telnet模式下无法发送中文字符,会出现乱码现象,如需发送中文字符请使用MobaXterm等第三方工具进行测试。
客户端
当客户端成功连接后,服务端控制台输出将由等待状态变为读取状态,等待当前客户端发送消息。

此时再新建一个客户端连接,服务端控制台输出依旧不会发生任何改变, 因为第一个客户端的资源处理还没有结束。

在第一个客户端中使用send命令向服务端发送消息(不熟悉telnet命令的话可以输入help查看)可以看到服务端已经接收到发送的 client1 字符串后输出,并且之前的第二个客户端的连接目前可以被处理,同样进入准备读取的状态。

使用第二个客户端发送消息,此时服务端正常输出。

优化
不难发现上述操作中存在的问题,服务端一次只能处理一个客户端的请求,其余的客户端只能等待,当某一个客户端请求的资源处理耗时较长时,对于其它的客户端使用是非常糟糕的,这里可以通过使用多线程方式来进行优化。
package com.mlyzr.bio;import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.StandardCharsets;/*** @author 勿忘初心* @since 2023-08-07-23:59* 同步阻塞IO示例*/
public class BioServer {public static void main(String[] args) throws IOException {// 监听本地8096端口ServerSocket serverSocket = new ServerSocket(8096);while(true){System.out.println("等待客户端连接...");// 接收客户端请求,阻塞Socket clientSocket = serverSocket.accept();System.out.println("监听到客户端连接");// 使用多线程来解决系统处理资源的能力new Thread(new Runnable(){@Overridepublic void run() {try {// NIO处理请求资源handler(clientSocket);}catch (Exception e){e.printStackTrace();}}}).start();}}public static void handler(Socket clientSocket) throws IOException {byte[] bytes = new byte[1024];System.out.println("准备读取");int read = clientSocket.getInputStream().read(bytes);System.out.println("读取完毕");if(read != -1){System.out.println("接收到来自客户端的数据 "+new String(bytes,0,read,StandardCharsets.UTF_8));}}
}
使用多线程后,可以看到服务端能够同时监听多个客户端的请求。
总结
使用改进后的代码通过测试可以发现服务端具备了同时处理多个客户端的能力,但同时仍然存在一些问题:
1.当请求非常大时,服务端将因为线程太多无法处理而导致崩溃。
2.即使使用了线程池,当线程池占满后服务将于单线程处理无异。
3.客户端只连接不发送数据,将一直占用服务端资源造成资源浪费。
NIO
概念
同步非阻塞IO,特点为当客户端发起请求后,在此期间客户端可以做其它的操作,但需要主动轮询服务端的处理结果,而且服务端也无需专注于当前请求的处理,也可以处理其他请求。
示例
package com.mlyzr.nio;import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;/*** @author 勿忘初心* @since 2023-08-08-15:28* 同步非阻塞IO示例代码*/
public class NioServer {public static void main(String[] args) throws IOException {// 保存客户端的Channel集合List<SocketChannel> channelList = new ArrayList<>();// 创建NIO的ServerSocketChannel,与BIO的ServerSocket类似ServerSocketChannel serverChannel = ServerSocketChannel.open();serverChannel.socket().bind(new InetSocketAddress(8096));// 设置channel为非阻塞serverChannel.configureBlocking(false);System.out.println("等待客户端连接");while(true){// 非阻塞模式的accept方法不会阻塞SocketChannel socketChannel = serverChannel.accept();// 客户端是否连接if(socketChannel != null){System.out.println("监听到客户端连接");socketChannel.configureBlocking(false);// 将客户端连接保存到list中channelList.add(socketChannel);}Iterator<SocketChannel> iterator = channelList.iterator();while(iterator.hasNext()){SocketChannel sc = iterator.next();ByteBuffer byteBuffer = ByteBuffer.allocate(128);int read = sc.read(byteBuffer);// 若存在数据,则将数据打印出来if(read > 0){System.out.println("接收到客户端数据"+new String(byteBuffer.array(),0,read,StandardCharsets.UTF_8));}// 无数据则说明客户端已断开if(read == -1){iterator.remove();System.out.println("客户端已断开连接");}}}}
}
客户端
运行实例代码后,可以同时打开多个客户端,客户端连接将被加入集合遍历处理,无需等待,服务端此时可以处理多个请求,此时服务端使用的是单线程。

优化
虽然上述代码解决了BIO中遗留的阻塞问题,但多个请求连接而不发送数据占用资源的情况仍然存在,如当请求数据量巨大时,需要通过遍历集合的方式寻找存在需要处理数据的客户端时,时间的损耗仍然非常大。因此使用多路复用的方式来解决无效遍历的问题。
package com.mlyzr.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.nio.charset.StandardCharsets;
import java.util.*;/*** @author 勿忘初心* @since 2023-08-08-15:28* 同步非阻塞IO示例代码*/
public class NioServer {public static void main(String[] args) throws IOException {// 创建NIO的ServerSocketChannel,与BIO的ServerSocket类似ServerSocketChannel serverChannel = ServerSocketChannel.open();serverChannel.socket().bind(new InetSocketAddress(8096));// 设置channel为非阻塞serverChannel.configureBlocking(false);// 引入多路复用,提高对Channel的处理能力, 即epollSelector selector = Selector.open();// 将ServerSocketChannel注册到selector上,即服务端接收到连接请求时,将连接事件进行注册SelectionKey selectionKey = serverChannel.register(selector,SelectionKey.OP_ACCEPT);System.out.println("等待客户端连接");while(true){// 判断是否有注册的事件,无事件将阻塞selector.select();// 获取所有已经注册的事件实例Set<SelectionKey> selectionKeys = selector.selectedKeys();Iterator<SelectionKey> keyIterator = selectionKeys.iterator();while(keyIterator.hasNext()){SelectionKey selectKey = keyIterator.next();// 若为连接事件,则获取该实例if(selectKey.isAcceptable()){ServerSocketChannel serverSocketChannel = (ServerSocketChannel) selectKey.channel();SocketChannel socketChannel = serverSocketChannel.accept();// 设置为非阻塞socketChannel.configureBlocking(false);// 注册读事件,若需要发送消息给客户端可以注册写事件SelectionKey key = socketChannel.register(selector,SelectionKey.OP_READ);System.out.println("监听到客户端连接");}if (selectKey.isReadable()){SocketChannel socketChannel = (SocketChannel) selectKey.channel();ByteBuffer byteBuffer = ByteBuffer.allocate(128);int read = socketChannel.read(byteBuffer);// 若存在数据,则将数据打印出来if(read > 0){System.out.println("接收到客户端数据"+new String(byteBuffer.array(),0,read,StandardCharsets.UTF_8));}// 无数据则说明客户端已断开if(read == -1){System.out.println("客户端已断开连接");socketChannel.close();}}// 从事件集合中删除本次处理的key,防止重复处理keyIterator.remove();}}}
}
总结
当使用多路复用后,会监听到客户端的连接事件,并为当前的连接注册读事件,当客户端发送数据时,会被再次监听,此时会进入读操作的事件处理中,将打印接收到的数据,同时每一次的事件使用后将会被移除,防止事件重复,从而解决了无效遍历的问题。
相关文章:
代码分析Java中的BIO与NIO
开发环境 OS:Win10(需要开启telnet服务,或使用第三方远程工具) Java版本:8 BIO 概念 BIO(Block IO),即同步阻塞IO,特点为当客户端发起请求后,在服务端未处理完该请求之前ÿ…...
网络安全(黑客)工作篇
一、网络安全行业的就业前景如何? 网络安全行业的就业前景非常广阔和有吸引力。随着数字化、云计算、物联网和人工智能等技术的迅速发展,网络安全的需求持续增长。以下是网络安全行业就业前景的一些关键因素: 高需求:随着互联网的…...
zookeeper入门学习
zookeeper入门学习 zookeeper应用场景 分布式协调组件 客户端第一次请求发给服务器2,将flag值修改为false,第二次请求被负载均衡到服务器1,访问到的flag也会是false 一旦有节点发生改变,就会通知所有监听方改变自己的值&#…...
VirtualEnv 20.24.0 发布
导读VirtualEnv 20.24.0 现已发布,VirtualEnv 用于在一台机器上创建多个独立的 Python 运行环境,可隔离项目之间的第三方包依赖,为部署应用提供方便,把开发环境的虚拟环境打包到生产环境即可,不需要在服务器上再折腾一…...
LabVIEW开发高压航空航天动力系统爬电距离的测试
LabVIEW开发高压航空航天动力系统爬电距离的测试 更多电动飞机MEA技术将发电,配电和用电集成到一个统一的系统中,提高了飞机的可靠性和可维护性。更多的电动飞机使用更多的电能来用电动替代品取代液压和气动系统。对车载电力的需求不断增加,…...
【论文阅读】基于深度学习的时序异常检测——Anomaly Transformer
系列文章链接 数据基础:多维时序数据集简介 论文一:2022 Anomaly Transformer:异常分数预测 论文二:2022 TransAD:异常分数预测 论文链接:Anomaly Transformer.pdf 代码链接:https://github.co…...
Java并发总结
1.创建线程三种方式 Runnable.Callable接口使用继承Thread类的方式创建多线程Runnable 和Callable区别 Callable规定(重写)的方法是call(),Runnable规定(重写)的方法是run()。Callable的任务执行后可返回值࿰…...
视频汇聚平台EasyCVR视频广场侧边栏支持拖拽
为了提升用户体验以及让平台的操作更加符合用户使用习惯,我们在EasyCVR v3.3版本中,支持面包屑侧边栏的广场视频、分组列表、收藏这三个模块拖拽排序,并且该操作在视频广场、视频调阅、电子地图、录像回放等页面均能支持。 TSINGSEE青犀视频…...
MyCat分片规则——范围分片、取模分片、一致性hash、枚举分片
1.范围分片 2.取模分片 范围分片和取模分片针对数字类型的字段可以,但是针对于字符串类型的字段时。这两种就不适用了。 3.一致性hash 4.枚举分片 默认节点指的是,如果我们向数据库表插入数据的时候,超出了这个枚举值,那么默认向…...
设计模式行为型——备忘录模式
目录 什么是备忘录模式 备忘录模式的实现 备忘录模式角色 备忘录模式类图 备忘录模式举例 备忘录模式代码实现 备忘录模式的特点 优点 缺点 使用场景 注意事项 实际应用 什么是备忘录模式 备忘录模式(Memento Pattern)又叫做快照模式&#x…...
Parquet存储的数据模型以及文件格式
文章目录 数据模型Parquet 的原子类型Parquet 的逻辑类型嵌套编码 Parquet文件格式 本文主要参考文献:Tom White. Hadoop权威指南. 第4版. 清华大学出版社, 2017.pages 363. Aapche Parquet是一种能有效存储嵌套数据的列式存储格式,在Spark中应用较多。 …...
Go和Java实现访问者模式
Go和Java实现访问者模式 我们下面通过一个解压和压缩各种类型的文件的案例来说明访问者模式的使用。 1、访问者模式 在访问者模式中,我们使用了一个访问者类,它改变了元素类的执行算法。通过这种方式,元素的执行算法可以随 着访问者改变而…...
想要通过软件测试的面试,都需要学习哪些知识
很多人认为,软件测试是一个简单的职位,职业生涯走向也不会太好,但是随着时间的推移,软件测试行业的变化,人们开始对软件测试行业的认知有了新的高度,越来越多的人开始关注这个行业,开始重视这个…...
MySQL的索引使用的数据结构,事务知识
一、索引的数据结构🌸 索引的数据结构(非常重要) mysql的索引的数据结构,并非定式!!!取决于MySQL使用哪个存储引擎 数据库这块组织数据使用的数据结构是在硬盘上的。我们平时写的代码是存在内存…...
普及100Hz高刷+1ms响应 微星发布27寸显示器:仅售799元
不论办公还是游戏,高刷及低响应时间都很重要,微星现在推出了一款27寸显示器PRO MP273A, 售价只有799元,但支持100Hz高刷、1ms响应时间,还有FreeSync技术减少撕裂。 PRO MP273A的100Hz高刷新率是其最大的卖点之一&#…...
Java课题笔记~6个重要注解参数含义
1、[掌握]Before 前置通知-方法有 JoinPoint 参数 在目标方法执行之前执行。被注解为前置通知的方法,可以包含一个 JoinPoint 类型参数。 该类型的对象本身就是切入点表达式。通过该参数,可获取切入点表达式、方法签名、目标对象等。 不光前置通知的方…...
Windows Docker Desk环境时区问题导致的时间问题解决?
大多docker镜像为了保持镜像大小,采用了alpine linux。 但经常由于时区问题导致时间不准确,解决也很简单。 1.查看事件文件 cd /usr/share/zoneinfo 2.复制时区文件 将文件copy到 /etc/localtime 路径下即可(重庆时区,上海也…...
SpringBoot复习:(22)ConfigurationProperties和@PropertySource配合使用及JSR303校验
一、配置类 package cn.edu.tju.config;import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.PropertySource; import org.springframework.stereotype.Component;Component ConfigurationPropertie…...
Spring IoC (控制反转)
IoC 是 Inversion of Control 的简写,译为“控制反转”,它不是一门技术,而是一种设计思想,是一个重要的面向对象编程法则。 Spring 通过 IoC 容器来管理所有 Java 对象的实例化和初始化,控制对象与对象之间的依赖关系。…...
安卓下模拟渲染EGLImageKHR
创建AHardwareBuffer并填充颜色 AHardwareBuffer_Desc desc = {static_cast<uint32_t>(screenW),static_cast<uint32_t>(screenH),...
深入剖析AI大模型:大模型时代的 Prompt 工程全解析
今天聊的内容,我认为是AI开发里面非常重要的内容。它在AI开发里无处不在,当你对 AI 助手说 "用李白的风格写一首关于人工智能的诗",或者让翻译模型 "将这段合同翻译成商务日语" 时,输入的这句话就是 Prompt。…...
2024年赣州旅游投资集团社会招聘笔试真
2024年赣州旅游投资集团社会招聘笔试真 题 ( 满 分 1 0 0 分 时 间 1 2 0 分 钟 ) 一、单选题(每题只有一个正确答案,答错、不答或多答均不得分) 1.纪要的特点不包括()。 A.概括重点 B.指导传达 C. 客观纪实 D.有言必录 【答案】: D 2.1864年,()预言了电磁波的存在,并指出…...
连锁超市冷库节能解决方案:如何实现超市降本增效
在连锁超市冷库运营中,高能耗、设备损耗快、人工管理低效等问题长期困扰企业。御控冷库节能解决方案通过智能控制化霜、按需化霜、实时监控、故障诊断、自动预警、远程控制开关六大核心技术,实现年省电费15%-60%,且不改动原有装备、安装快捷、…...
postgresql|数据库|只读用户的创建和删除(备忘)
CREATE USER read_only WITH PASSWORD 密码 -- 连接到xxx数据库 \c xxx -- 授予对xxx数据库的只读权限 GRANT CONNECT ON DATABASE xxx TO read_only; GRANT USAGE ON SCHEMA public TO read_only; GRANT SELECT ON ALL TABLES IN SCHEMA public TO read_only; GRANT EXECUTE O…...
Qt Http Server模块功能及架构
Qt Http Server 是 Qt 6.0 中引入的一个新模块,它提供了一个轻量级的 HTTP 服务器实现,主要用于构建基于 HTTP 的应用程序和服务。 功能介绍: 主要功能 HTTP服务器功能: 支持 HTTP/1.1 协议 简单的请求/响应处理模型 支持 GET…...
Cloudflare 从 Nginx 到 Pingora:性能、效率与安全的全面升级
在互联网的快速发展中,高性能、高效率和高安全性的网络服务成为了各大互联网基础设施提供商的核心追求。Cloudflare 作为全球领先的互联网安全和基础设施公司,近期做出了一个重大技术决策:弃用长期使用的 Nginx,转而采用其内部开发…...
智能AI电话机器人系统的识别能力现状与发展水平
一、引言 随着人工智能技术的飞速发展,AI电话机器人系统已经从简单的自动应答工具演变为具备复杂交互能力的智能助手。这类系统结合了语音识别、自然语言处理、情感计算和机器学习等多项前沿技术,在客户服务、营销推广、信息查询等领域发挥着越来越重要…...
C#中的CLR属性、依赖属性与附加属性
CLR属性的主要特征 封装性: 隐藏字段的实现细节 提供对字段的受控访问 访问控制: 可单独设置get/set访问器的可见性 可创建只读或只写属性 计算属性: 可以在getter中执行计算逻辑 不需要直接对应一个字段 验证逻辑: 可以…...
【从零学习JVM|第三篇】类的生命周期(高频面试题)
前言: 在Java编程中,类的生命周期是指类从被加载到内存中开始,到被卸载出内存为止的整个过程。了解类的生命周期对于理解Java程序的运行机制以及性能优化非常重要。本文会深入探寻类的生命周期,让读者对此有深刻印象。 目录 …...
Java后端检查空条件查询
通过抛出运行异常:throw new RuntimeException("请输入查询条件!");BranchWarehouseServiceImpl.java // 查询试剂交易(入库/出库)记录Overridepublic List<BranchWarehouseTransactions> queryForReagent(Branch…...


