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

news2025/6/24 2:43:47

目录

    • 一、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.coloradmin.cn/o/1086174.html

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

相关文章

day62:ARMday9,I2c总线通信

作业&#xff1a;按键中断实现LED1、蜂鸣器、风扇 key_in.c: #include "key_in.h"void gpio_init() {//RCC使能//GPIOERCC->MP_AHB4ENSETR | (0x1<<4);//GPIOBRCC->MP_AHB4ENSETR | (0x1<<1);//PE10、PB6、PE9输出模式GPIOE->MODER & ~(0…

经典面试题第八更---reduce的使用

前言&#xff1a; &#x1f921; 作者简介&#xff1a;我是Morning&#xff0c;计算机的打工人&#xff0c;想要翻身做主人 &#x1f648; &#x1f648; &#x1f648; &#x1f3e0; 个人主页&#xff1a; Morning的主页 &#x1f4d5;系列专栏&#xff1a;前端面…

ARM day9

src/key_it.c #include "key_it.h" #include "led.h" void key_it_config() {//RCC使能GPIOF时钟RCC->MP_AHB4ENSETR | (0x1<<5);//设置PF9 PF7 PF8GPIO输入//PF9GPIOF->MODER & (~(0x3<<18));//PF8GPIOF->MODER & (~(0x3&l…

Unity - Normal mapping - Reoriented normal mapping - 重定向法线、混合法线

文章目录 目的核心代码PBR - Filament - Normal mappingShader效果BlendNormal_Hill12BlendNormal_UDNBlendNormals_Unity_Native - 效果目前最好 ProjectReferences 目的 备份、拾遗 核心代码 half3 blended_normal normalize(half3(n1.xy n2.xy, n1.z*n2.z));PBR - Filam…

不是钉钉管理员如何批量复制公司全部人员名单到execl表格里

环境&#xff1a; Win10 专业版 钉钉V7.10.0 问题描述&#xff1a; 不是钉钉管理员,如何批量复制公司全员群里面全面人员名单 公司人员有388多个 解决方案&#xff1a; 1.打开公司全员群&#xff0c;右上角点开设置&#xff0c;点查看全部群成员 2.右侧成员一次复制几个&…

Vue-2.7自定义指令

自定义指令 自己定义的指令&#xff0c;可以封装一些dom操作&#xff0c;扩展额外功能 例如需求&#xff1a;当页面加载时&#xff0c;让元素将获得焦点&#xff08;autofucus在safari浏览器有兼容性&#xff09; 操作dom&#xff1a;dom元素.focus() 太麻烦&#xff01;…

datax同步数据简介

概述 业务中经常会用到数据全量同步和增量同步&#xff0c;用sqlDump只能全量同步&#xff0c;而且数据量大的时候很慢。 阿里的datax目前是一款不错的同步工具 环境要求&#xff1a; jdk:1.8 python:2.7 maven:3.0 3.0的python跑不起来 ps:开源版的datax的mysql驱动还是用…

“零代码”能源管理平台:智能管理能源数据

随着能源的快速增长&#xff0c;有效管理和监控能源数据变得越来越重要。为了帮助企业更好的管理能源以及降低能源成本&#xff0c;越来越多的能源管理平台出现在市面上。 “零代码”形式的能源管理平台&#xff0c;采用IT与OT深度融合为理念&#xff0c;可进行可视化、拖拽、…

【C语言】每日一题(半月斩)——day4

目录 选择题 1、设变量已正确定义&#xff0c;以下不能统计出一行中输入字符个数&#xff08;不包含回车符&#xff09;的程序段是&#xff08; &#xff09; 2、运行以下程序后&#xff0c;如果从键盘上输入 65 14<回车> &#xff0c;则输出结果为&#xff08; &…

RabbitMQ消息中间件概述

1.什么是RabbitMQ RabbitMQ是一个由erlang开发的AMQP&#xff08;Advanced Message Queue &#xff09;的开源实现。AMQP 的出现其实也是应了广大人民群众的需求&#xff0c;虽然在同步消息通讯的世界里有很多公开标准&#xff08;如 COBAR的 IIOP &#xff0c;或者是 SOAP 等&…

皕杰报表中填报控件显示模式控制问题

填报报表是指能够向数据库上传数据的报表&#xff0c;皕杰报表填报单元格支持多种控件类型&#xff0c;包括文本框、文本域、下拉框、下拉树、单选框、复选框、时间、日期、日期时间… 这些控件类型在web页面上的显示模式在皕杰报表里设置了三种模式&#xff0c;只读、显式显示…

游戏反虚拟机检测方案

近年来&#xff0c;游戏市场高速发展&#xff0c;随之而来的还有图谋利益的游戏黑产。在利益吸引下&#xff0c;游戏黑产扩张迅猛&#xff0c;攻击趋势呈现出角度多样化的特点。 在这一趋势下&#xff0c;游戏安全防护的检测覆盖率显得尤为重要。如果游戏在某一环节出现被绕过…

小白网络安全学习手册

作为一个合格的网络安全工程师&#xff0c;应该做到攻守兼备&#xff0c;毕竟知己知彼&#xff0c;才能百战百胜。 谈起黑客&#xff0c;可能各位都会想到&#xff1a;盗号&#xff0c;其实不尽然&#xff1b;黑客是一群喜爱研究技术的群体&#xff0c;在黑客圈中&#xff0c;一…

毫末AI DAY的智驾弹药:上有「世界模型」,下有3000元方案

作者 | 张祥威 编辑 | 德新 「我们的感知能力可以说能做到识别万物了。」毫末智行在第九届毫末AI DAY上抛出这一豪言。 识别万物的底气&#xff0c;源于毫末的DriveGPT自动驾驶大模型引入了通用语义大模型&#xff0c;可以对交通参与者者、道路环境等做到全面感知。 今年是智…

python pip安装超时使用国内镜像

网络环境差的时候需要我们独立的进行相对应的包下载离线安装&#xff0c;或者给pip 加上 国内的镜像源比如加上清华的镜像源&#xff1a; 参考网址&#xff1a;pypi | 镜像站使用帮助 | 清华大学开源软件镜像站 | Tsinghua Open Source Mirror https://mirrors.tuna.tsinghua…

简单写个JS插件替换网页上的文本

背景&#xff1a; 使用Grafana监控面板&#xff0c;随着项目迭代&#xff0c;页面上几百个图表了&#xff0c;相当一部分和错误码有关&#xff0c;逐个图表单替换维护过于繁琐&#xff0c;于是尝试一键替换grafana面板上的错误码(也就替换网页上特定的文本元素)&#xff0c;原…

禅道如何编辑项目模块

禅道是一个用于项目管理和软件开发的开源工具&#xff0c;它的主要作用包括&#xff1a; 1. 项目管理&#xff1a;禅道可以帮助项目团队进行任务分配、进度跟踪、沟通协作、测试管理等方面的工作&#xff0c;以确保项目能够按时完成。 2. 缺陷追踪&#xff1a;禅道可以记录和…

钉钉数字校园小程序开发:开启智慧教育新时代

随着信息技术的快速发展和校园管理的日益复杂化&#xff0c;数字校园已成为现代教育的重要趋势。钉钉数字校园小程序作为一种创新应用&#xff0c;以其专业性、思考深度和逻辑性&#xff0c;为学校提供了全新的管理、教学和沟方式。本文从需求分析、技术实现和应用思考三个方面…

JAVA学习第一天,java的运行方式

对未来很迷茫&#xff0c;不知道以后能出去干什么&#xff0c;好像掌握的东西很少&#xff0c;从今天开始学习学习java吧&#xff0c;让自己充实起来&#xff0c;记录一下。 jav…

QT快速下载

官网下载安装包后&#xff0c;打开命令行CMD&#xff0c;将qt安装包拖入cmd中 添加–mirror https://mirrors.tuna.tsinghua.edu.cn/qt 回车 按安装步骤安装即可