Java BIO模型分析(提供单线程和多线程服务端代码示例)

目录

    • 一、BIO特点介绍
    • 二、BIO代码实现
      • 2.1、客户端代码准备
      • 2.2、服务端单线程处理
        • 2.2.1、服务端代码
        • 2.2.2、阻塞代码分析
        • 2.2.3、存在问题
      • 2.3、服务端多线程处理
        • 2.3.1、服务端代码
        • 2.3.2、存在问题

一、BIO特点介绍

  • BIO(blocking I/O):同步阻塞IO,在每个I/O操作(如读取或写入)都会导致线程被阻塞,直到操作完成。
  • BIO方式适用于连接数目比较小且固定的架构,这种模型适合于较低的并发需求,每个连接通常都需要一个独立的线程,也可以使用线程池管理,JDK1.4以前的唯一选择,程序简单易理解。
  • BIO是基于字节流和字符流进行操作的。
  • 使用java.io包中的类,如InputStream和OutputStream,它们提供了阻塞式的I/O操作。

二、BIO代码实现

2.1、客户端代码准备

这里准备一个通用的客户端,会给服务端发送两条消息,发送时间间隔2秒,用于做后面的测试。

public class BioClient{public static void main(String[] args) {for (int i=0;i<1;i++) {// 这里使用多线程处理,为了适配多线程服务端new Thread(()->{BioClient bioClient = new BioClient();try {bioClient.run();} catch (Exception e) {e.printStackTrace();}}).start();}}public void run() throws Exception{Socket socket = null;InputStream inputStream = null;OutputStream outputStream = null;BufferedReader br = null;try {socket = new Socket("127.0.0.1", 9998);System.out.println(getTime() + "和服务端建立连接");Thread.sleep(2000);outputStream = socket.getOutputStream();PrintStream ps = new PrintStream(outputStream);System.out.println(getTime() + "给服务端发送消息:hello");ps.println("hello");Thread.sleep(2000);System.out.println(getTime() + "间隔2秒在给服务端发送消息:&end");ps.println("&end");ps.flush();inputStream = socket.getInputStream();br = new BufferedReader(new InputStreamReader(inputStream));String s = null;System.out.println(getTime() + "阻塞等待服务端发送给客户端的数据");while ((s = br.readLine()) != null) {System.out.println(getTime() + Thread.currentThread().getName()+" 接收到服务端的数据:" + s);}} finally {br.close();inputStream.close();outputStream.close();socket.close();}}public static String getTime(){return "time=" + System.currentTimeMillis()/1000 + "\t";}
}

2.2、服务端单线程处理

在这里插入图片描述

2.2.1、服务端代码
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;public class BioServer {public static void main(String[] args) throws IOException {ServerSocket serverSocket = new ServerSocket(9998);while (true) {// 阻塞等待socket连接Socket accept = serverSocket.accept();System.out.println(getTime() + "建立连接 port=" + accept.getPort());InputStream inputStream = accept.getInputStream();BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));//连接后阻塞等待连接数据输入String s = null;while ((s = br.readLine()) != null) {System.out.println(getTime() + Thread.currentThread().getName() + " 接收到的数据:" + s);// 当收到&end消息时给客户端发送一个消息,并且关闭流跳出当前循环if ("&end".equals(s)) {PrintStream ps = new PrintStream(accept.getOutputStream());ps.println("早点睡");ps.flush();System.out.println(getTime() + "收到&end消息时给客户端发送一个消息完毕");br.close();inputStream.close();accept.close();break;}}System.out.println(getTime() + "一次连接处理完成,等待下一个连接");System.out.println("-----------------------------------------------------------");}}public static String getTime() {return "time=" + System.currentTimeMillis() / 1000 + "\t";}
}
2.2.2、阻塞代码分析

BIO有两个地方会阻塞线程,第一个就是在serverSocket.accept();时,当没有连接建立时会一直等待,当有连接进入时开始执行后续流程,我这个例子中建立连接后会获取输入流读取客户端发送的消息,在调用br.readLine()方法真正读取数据时,如果客户端还没有发送消息只是建立了连接,这一步也是会阻塞的,我在客户端建立socket连接后会暂停2秒才给服务端发送消息,所以这里会阻塞2s,当客户端暂停结束后会给服务端发送一条消息,当消息发送到服务端,服务端br.readLine()方法读取到数据开始执行后续流程,因为这里是循环读取的所以第一条消息读取完之后又会执行br.readLine()方法,这时又会阻塞等待客户端消息,客户端发送第一条消息后间隔2s会再次给服务端发送一条消息,消息内容为&end,服务端br.readLine()方法读取到第二条数据后判断消息内容是否为&end,如果为&end则给客户端发送一条早点睡,发送完毕后会将流和连接全部关闭并且跳出循环,等待下一个连接。

2.2.3、存在问题
  • BIO单线程同时只能处理一个连接,当调用br.readLine()方法读取数据时会阻塞,只有当连接断开或者读取到一次数据后才会执行后续逻辑,也就是说如果连接没有断开,客户端一直不给服务端发送消息那么服务端就会一直阻塞,我们这里的代码读取一次数据后还会继续循环读取直到读到&end才会自己跳出循环,如果不跳出循环连接不断开则其它连接处理。
  • BIO一般情况下无法使用一个线程处理多个连接,其实也是可以的,设想我们将获取到的socket存储在数组里,每收到两个socket再去处理,这样就能一个线程处理两个socket连接了,但是肯定不会这样做,因为serverSocket.accept();会阻塞,如果只来了一个连接,那么这个连接就一直不会处理了。
  • 针对这些问题,竟然单线程无法处理那么下面使用多线程处理,继续分析。

2.3、服务端多线程处理

在这里插入图片描述

2.3.1、服务端代码
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;public class BioConcurrentServer {public static void main(String[] args) throws IOException {ServerSocket serverSocket = new ServerSocket(9998);ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2, 2, 10, TimeUnit.SECONDS, new LinkedBlockingQueue<>(100));while (true) {// 阻塞等待socket连接Socket accept = serverSocket.accept();System.out.println(getTime() + "建立连接 port=" + accept.getPort());// 将socket连接交由线程池处理threadPoolExecutor.execute(()->{try {System.out.println(getTime() + "开始处理socket消息");run(accept);} catch (Exception e) {throw new RuntimeException(e);}});}}public static void run(Socket accept) throws Exception {InputStream inputStream = accept.getInputStream();BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));//连接后阻塞等待连接数据输入String s = null;while ((s = br.readLine()) != null) {System.out.println(getTime() + Thread.currentThread().getName() + " 接收到的数据:" + s);// 当收到&end消息时给客户端发送一个消息,并且关闭流跳出当前循环if ("&end".equals(s)) {PrintStream ps = new PrintStream(accept.getOutputStream());ps.println("早点睡");ps.flush();System.out.println(getTime() + "收到&end消息时给客户端发送一个消息完毕");br.close();inputStream.close();accept.close();break;}}System.out.println(getTime() + "一次连接处理完成,等待下一个连接");System.out.println("-----------------------------------------------------------");}public static String getTime() {return "time=" + System.currentTimeMillis() / 1000 + "\t";}
}
2.3.2、存在问题
  • 在接收到客户端连接后使用线程池处理 Read / Write ,这样就能同时处理多个连接,看着好像没有什么问题,但是仔细想想如果客户端只是建立了连接没有给服务端发送消息,那么服务端在子线程调用br.readLine()时是不是会一直阻塞,那么就会一直占用线程,这也就说明了为什么BIO中通常一个连接就需要一个线程。

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

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

相关文章

【UnityUGUI】复合控件详解,你还记得多少

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a;UI_…

linux总结

cat -n filename 查看文件,-n用来给每一行标行号,可以省略 cat /var/log/mysqld.log | grep password 我们可以通过上述指令&#xff0c;查询日志文件内容中包含password的行信息。 more 作用: 以分页的形式显示文件内容 语法: more fileName 操作说明: 回车键 …

企架布道:中电金信应邀出席2023佛山敏捷之旅暨DevOps Meetup

近日&#xff0c;2023佛山敏捷之旅暨DevOps Meetup活动顺利举行&#xff0c;本次活动以助力大湾区金融和互联网企业敏捷DevOps实施和效能提升为主题&#xff0c;共设立 2个会场&#xff0c;16个话题分享&#xff0c;200余位金融、互联网企业相关从业人员齐聚一堂&#xff0c;共…

代码随想录Day15 二叉树 LeetCodeT513 找树左下角的值 T112路径总和 T106 从中序和后序遍历构造二叉树

以上思路来自于:代码随想录 (programmercarl.com) LeetCode T513 找树左下角的值 题目思路: 本题思路:这题我们使用递归法和迭代法解决问题 注意:左下角的值不一定就是一直向左遍历的叶子结点的值,首先可以确定是最后一行的第一个叶子结点的值,也就是最大深度的叶子结点的值 定…

如何打造一个网络框架模块对接服务器

一、了解网络框架的基本原理 在开始打造网络框架模块之前&#xff0c;首先需要了解网络框架的基本原理。网络框架是一个软件模块&#xff0c;用于处理网络通信的各种细节&#xff0c;包括数据传输、协议解析、错误处理等。常见的网络框架有HTTP、TCP/IP、WebSocket等。 对啦&…

【error】root - Exception during pool initialization

报错提示&#xff1a;root - Exception during pool initialization. 错误原因&#xff1a; 配置数据库出错 我的错误配置&#xff1a; spring.datasource.urljdbc:mysql://localhost:3306/springboot?serverTimezoneGMT spring.datasource.nameroot spring.datasource.pass…

Flink---11、状态管理(按键分区状态(值状态、列表状态、Map状态、归约状态、聚合状态)算子状态(列表状态、广播状态))

星光下的赶路人star的个人主页 这世上唯一扛得住岁月摧残的就是才华 文章目录 1、状态管理1.1 Flink中的状态1.1.1 概述1.1.2 状态的分类 1.2 按键分区状态&#xff08;Keyed State&#xff09;1.2.1 值状态&#xff08;ValueState&#xff09;1.2.2 列表状态&#xff08;ListS…

基于NLopt的C语言非线性优化案例

以官方给的例程&#xff0c;重新梳理&#xff0c;以供理解NLopt的使用。 问题被定义为&#xff1a; min ⁡ x ∈ R 2 x 2 s u b j e c t t o x 2 ≥ 0 , x 2 ≥ ( a 1 x 1 b 1 ) 3 , a n d x 2 ≥ ( a 2 x 1 b 2 ) 3 f o r p a r a m e t e r s a 1 2 , b 1 0 , a 2 − 1…

JavaScript使用类-模态窗口

**上节课我们为这个项目获取了一些DOM元素&#xff0c;现在我们可以继续&#xff1b;**这个模态窗口有一个hidden类&#xff0c;这个类上文我们讲了&#xff0c;他的display为none&#xff1b;如果我们去除这个hidden的话&#xff0c;就可以让这个模态窗口展现出来。如下 cons…

【Debian系统】:安装debian系统之后,很多命令找不到,需要添加sudo之后才能使用,以下解决方法

项目场景&#xff1a; 问题描述 解决方案&#xff1a; 1.临时解决方案 2.永久解决方案 1.首先打开编辑&#xff1a; 2.打开之后最后一行添加代码&#xff1a; 3.最后运行一遍 .bashrc 4.已经可以了&#xff0c;可以试试reboot&#xff0c;重启一下机子 一点一滴才能成长 …

windows的最佳选项卡式窗口管理器软件TidyTabs

下载&#xff1a; https://jmj.cc/s/z1t3kt?pucodeS1wc https://download.csdn.net/download/mo3408/88420433 TidyTabs是一款Windows应用程序&#xff0c;它可以将多个打开的窗口整理成一个选项卡式的界面&#xff0c;使得用户可以更加方便地切换和管理不同的窗口。 Tidy…

京东商品数据:8月京东环境电器行业数据分析

8月份&#xff0c;环境电器大盘市场整体下滑。鲸参谋数据显示&#xff0c;8月京东平台环境电器的大盘将近570万&#xff0c;环比下滑约29%&#xff0c;同比下滑约10%&#xff1b;销售额为25亿&#xff0c;环比下滑约23%&#xff0c;同比下滑约8%。 *数据源于鲸参谋-行业趋势分析…

网工内推 | 技术支持工程师,厂商公司,HCIA即可,有带薪年假

01 华为终端有限公司 招聘岗位&#xff1a;初级技术支持 职责描述&#xff1a; 1、通过远程方式处理华为用户在产品使用过程中各种售后问题&#xff1b; 2、收集并整理消费者声音&#xff0c;提供服务持续优化建议&#xff1b; 3、对服务中发现的热点、难点问题及其他有可能造…

iMazing2023免费版苹果iPhone手机备份应用软件

iMazing是一款功能强大的苹果手机备份软件&#xff0c;它可通过备份功能将通讯录备份到电脑上&#xff0c;并在电脑端iMazing“通讯录”功能中随时查看和导出联系人信息。它自带Wi-Fi自动备份功能&#xff0c;能够保证通讯录备份数据是一直在动态更新的&#xff0c;防止手机中新…

收银系统商品定价设计思考

一、背景 因为门店系统里商品总共也就几万款&#xff0c;一直以来都是根据条码由总部统一定价销售&#xff0c;现在有加盟店&#xff0c;各门店也有进行各自促销活动的需求&#xff0c;这就需要放开门店自主定价权&#xff0c;所以近段时间系统在商品定价上做了扩展。 二、商…

【SCS-CN】SCS-CN模型中CN值的确定

目录 一、说明二、SWAT三、HEC-HMS四、CN值转换公式五、确定CN25.1 ArcSWAT 2009用户指南5.2 SWAT plus Document5.3 National Engineering Handbook5.4 HEC-HMS水文建模系统原理方法应用5.5 Technical Release 55 (TR-55) 六、确定水文土壤单元&#xff08;HSG&#xff09;6.1…

移动应用-Android开发基础\核心知识点

Android开发基础 知识点 1 介绍了解2 系统体系架构3 四大应用组件4 移动操作系统优缺点5 开发工具6 配置工具7 下载相关资源8JDK下载安装流程9配置好SDK和JDK环境10 第一个Hello word11 AS开发前常用设置12模拟器使用运行13 真机调试14 AndroidUI基础布局15 加载展示XML布局16…

JS VUE 用 canvas 给图片加水印

最近写需求&#xff0c;遇到要给图片加水印的需求。 刚开始想的方案是给图片上覆盖一层水印照片&#xff0c;但是这样的话用户直接下载图片水印也会消失。 后来查资料发现用 canvas 就可以给图片加水印&#xff0c;下面是处理过程。 首先我们要确认图片的格式&#xff0c;我们通…

华为云云耀云服务器L实例评测|使用redis事务和lua脚本

文章目录 云服务器的类型云服务优点redis一&#xff0c;关系型数据库&#xff08;sqlserver&#xff0c;mysql&#xff0c;oracle&#xff09;的事务隔离机制说明&#xff1a;redis事务机制 lualua脚本好处&#xff1a;一&#xff0c;怎么在redis中使用lua脚本二&#xff0c;脚…

【网络安全】「漏洞原理」(一)SQL 注入漏洞之概念介绍

前言 严正声明&#xff1a;本博文所讨论的技术仅用于研究学习&#xff0c;旨在增强读者的信息安全意识&#xff0c;提高信息安全防护技能&#xff0c;严禁用于非法活动。任何个人、团体、组织不得用于非法目的&#xff0c;违法犯罪必将受到法律的严厉制裁。 【点击此处即可获…