当前位置: 首页 > article >正文

Java BIO详解

一、简介

1.1 BIO概述

BIO(Blocking I/O),即同步阻塞IO(传统IO)。

 BIO 全称是 Blocking IO,同步阻塞式IO,是JDK1.4之前的传统IO模型,就是传统的 java.io 包下面的代码实现。

服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如下图所示:

在 BIO 模型下,应用程序会在进行 I/O 操作时阻塞当前线程,直到 I/O 操作完成。例如,执行一个读取操作时,线程会等待,直到数据从磁盘或网络中完全读取完成。在这个过程中,线程不能做其他任务,必须等待 I/O 操作的结果。

BIO 模型的特点

  • 同步阻塞:
    • 当线程进行 I/O 操作时,它会被阻塞,直到操作完成。
    • 阻塞操作通常会导致 CPU 的浪费,因为线程在等待 I/O 时并没有进行其他有用的工作。
  • 一个连接一个线程:
    • 每个客户端请求都会创建一个新的线程,每个线程对应一个 I/O 操作。
    • 当并发连接数很多时,系统可能会因为线程数过多而导致性能瓶颈。
  • 适用于连接数较少的场景:BIO 更适用于连接数较少、请求不频繁的应用场景,如一些小型应用或传统的阻塞式通信。
  • 容易实现:相对于其他 I/O 模型(如 NIO 和 AIO),BIO 的实现比较简单,应用开发人员只需要关心 I/O 操作,不需要处理复杂的事件驱动机制。

BIO 的缺点:

  • 线程资源浪费:每个连接都会对应一个独立的线程,当有大量并发连接时,会导致系统开销巨大,因为操作系统会为每个线程分配资源(如内存、栈空间等)。如果并发请求量很大,线程上下文切换开销也会非常高。
  • 不适合高并发:BIO 模型非常依赖操作系统线程,线程数过多时容易造成系统性能下降。特别是在高并发的情况下,线程的创建和销毁频繁,容易耗尽系统资源。
  • 效率低:在 I/O 操作过程中,线程被阻塞,无法处理其他请求,导致 CPU 的浪费。即使没有数据可读或可写,线程依然会等待,直到 I/O 完成。

1.2 IO流概述

IO流是基于流的概念,它将数据的输入和输出看作是一个连续的流。数据从一个地方流向另一个地方,流的方向可以是输入(读取数据)或输出(写入数据)。

IO流的原理是通过流的管道将数据从源头传输到目标地。源头可以是文件、网络连接、内存等,而目标地可以是文件、数据库、网络等。IO流提供了一组丰富的类和方法来实现不同类型的输入和输出操作。

IO流主要用于处理输入和输出操作,适用于以下场景:

  • 文件读写:通过IO流可以读取和写入文件中的数据,如读取配置文件、写入日志等。
  • 网络通信:通过IO流可以进行网络数据的传输和接收,如Socket通信、HTTP请求等。
  • 数据库操作:通过IO流可以将数据读取到内存中,或将内存中的数据写入到数据库中。
  • 文本处理:通过IO流可以读取和写入文本文件,进行文本处理和操作。

1.2.1 IO流分类

Java中的IO流可以按照数据的类型和流的方向进行分类:

  • 按数据类型分类
    • 字节流(Byte Stream):以字节为单位读写数据,适用于处理二进制数据,如图像、音频、视频等。常见的字节流类有InputStream和OutputStream。
    • 字符流(Character Stream):以字符为单位读写数据,适用于处理文本数据。字符流会自动进行字符编码和解码,可以处理多国语言字符。常见的字符流类有Reader和Writer。
  • 按流的方向分类
    • 输入流(Input Stream):用于读取数据。输入流从数据源读取数据,如文件、网络连接等。常见的输入流类有FileInputStream、ByteArrayInputStream、SocketInputStream等。
    • 输出流(Output Stream):用于写入数据。输出流将数据写入到目标地,如文件、数据库、网络等。常见的输出流类有FileOutputStream、ByteArrayOutputStream、SocketOutputStream等。

1.2.1.1 字符流
  • 只用来处理文本数据 ;
  • 数据最常见的表现形式是文件,字符流用来操作文件的子类一般是 FileReader 和 FileWriter 。

字符流读写文件注意事项:

  • 写入文件必须要用 flush() 刷新 ;
  • 用完流记得要关闭流 ;
  • 使用流对象要抛出IO异常 ;
  • 定义文件路径时,可以用"/"或者"\" ;
  • 在创建一个文件时,如果目录下有同名文件将被覆盖 ;
  • 在读取文件时,必须保证该文件已存在,否则抛出异常 ;
  • 字符流的缓冲区 ;
  • 缓冲区的出现是为了提高流的操作效率而出现的 ;
  • 需要被提高效率的流作为参数传递给缓冲区的构造函数 ;
  • 在缓冲区中封装了一个数组,存入数据后一次取出 。
1.2.1.2 字节流
  • 用来处理媒体数据 。

字节流读写文件注意事项:

  • 字节流和字符流的基本操作是相同的,但是想要操作媒体流就需要用到字节流 ;
  • 字节流因为操作的是字节,所以可以用来操作媒体文件(媒体文件也是以字节存储的);
  • 输入流(InputStream)、输出流(OutputStream);
  • 字节流操作可以不用刷新流操作 ;
  • InputStream特有方法:int available()(返回文件中的字节数);
  • 字节流的缓冲区 ;
  • 字节流缓冲区跟字符流缓冲区一样,也是为了提高效率 。

1.2.2 常用IO流

1.2.2.1 字节输入流类
  • InputStream:用于从输入源读取字节数据的抽象类。
  • FileInputStream:从文件中读取字节数据的类。
  • ByteArrayInputStream:从字节数组中读取字节数据的类。
  • BufferedInputStream:提供缓冲功能的字节输入流类。
  • DataInputStream:读取基本数据类型的字节输入流类。
try (InputStream is = new FileInputStream("input.txt");BufferedInputStream bis = new BufferedInputStream(is)) {int data;while ((data = bis.read()) != -1) {System.out.print((char) data);}
} catch (IOException e) {e.printStackTrace();
}
1.2.2.2 字符输入流类
  • Reader:用于从输入源读取字符数据的抽象类。
  • FileReader:从文件中读取字符数据的类。
  • BufferedReader:提供缓冲功能的字符输入流类。
  • InputStreamReader:将字节流转换为字符流的类。
try (Reader reader = new FileReader("input.txt");BufferedReader br = new BufferedReader(reader)) {String line;while ((line = br.readLine()) != null) {System.out.println(line);}
} catch (IOException e) {e.printStackTrace();
}
1.2.2.3 字节输出流类
  • OutputStream:用于向输出目标写入字节数据的抽象类。
  • FileOutputStream:将字节数据写入文件的类。
  • ByteArrayOutputStream:将字节数据写入字节数组的类。
  • BufferedOutputStream:提供缓冲功能的字节输出流类。
  • DataOutputStream:将基本数据类型写入输出流的类。
try (OutputStream os = new FileOutputStream("output.txt");BufferedOutputStream bos = new BufferedOutputStream(os)) {String data = "Hello, World!";bos.write(data.getBytes());
} catch (IOException e) {e.printStackTrace();
}
1.2.2.4 字符输出流类
  • Writer:用于向输出目标写入字符数据的抽象类。
  • FileWriter:将字符数据写入文件的类。
  • BufferedWriter:提供缓冲功能的字符输出流类。
  • OutputStreamWriter:将字节流转换为字符流的类。
try (Writer writer = new FileWriter("output.txt");BufferedWriter bw = new BufferedWriter(writer)) {String data = "Hello, World!";bw.write(data);
} catch (IOException e) {e.printStackTrace();
}

1.2.3 Java Scanner类

Java 5添加了 java.util.Scanner 类,这是一个用于扫描输入文本的新的实用程序。

  1. 关于 nextInt()、next()、nextLine() 的理解 :
  • nextInt() :只能读取数值,若是格式不对,会抛出 java.util.InputMismatchException 异常 ;
  • next() :遇见第一个有效字符(非空格,非换行符)时,开始扫描,当遇见第一个分隔符或结束符(空格或换行符)时,结束扫描,获取扫描到的内容 ;
  • nextLine() :可以扫描到一行内容并作为字符串而被捕获到 。
  1. 关于 hasNext()、hasNextLine()、hasNextxxx() 的理解 :就是为了判断输入行中是否还存在xxx的意思。
  2. 与 delimiter() 有关的方法的理解 :应该是输入内容的分隔符设置,

二、BIO工作机制

客户端:

  • 通过Socket对象请求与服务端建立连接。
  • 从Socket中得到字节输入或者字节输出流进行数据读写操作。

服务端:

  • 通过ServerSocket注册端口。
  • 服务端通过调用accept方法用于监听客户端的Socket请求。
  • 从Socket中得到字节输入或者字节输出流进行数据读写操作。

三、简单编码实现

3.1 服务端

public class BioServer {public static void main(String[] args) {ServerSocket serverSocket = null;try {serverSocket = new ServerSocket(8080);System.out.println("Server is listening on port 8080...");while (true) {// 阻塞等待客户端连接Socket clientSocket = serverSocket.accept();System.out.println("Client connected: " + clientSocket.getInetAddress());// 为每个客户端请求创建一个新线程进行处理new Thread(new ClientHandler(clientSocket)).start();}} catch (IOException e) {e.printStackTrace();} finally {try {if(serverSocket != null){serverSocket.close();}} catch (IOException e){e.printStackTrace();}}}
}class ClientHandler implements Runnable {private Socket socket;public ClientHandler(Socket socket) {this.socket = socket;}@Overridepublic void run() {BufferedInputStream bufferedInputStream = null;BufferedOutputStream bufferedOutputStream  = null;try {bufferedInputStream = new BufferedInputStream(socket.getInputStream());;bufferedOutputStream  = new BufferedOutputStream(socket.getOutputStream());System.out.println("收到来自客户端的消息:");byte[] bytes = new byte[1024]; //创建字节数组,存储临时读取的数据int len ; //记录数据读取的长度if ((len = bufferedInputStream.read(bytes)) > -1) { //长度为-1则读取完毕String result = new String(bytes,0,len);System.out.println(result);}String outString = "服务端收到,这里是线程" + Thread.currentThread().getName();System.out.println("回复信息给客户端: " + outString);bufferedOutputStream.write(outString.getBytes());bufferedOutputStream.flush();System.out.println("回复完成========");} catch (IOException e) {e.printStackTrace();} finally {System.out.println("关闭数据流========");try {if (bufferedInputStream != null) {bufferedInputStream.close();}if (bufferedOutputStream != null) {bufferedOutputStream.close();}} catch (IOException e){e.printStackTrace();}}}
}

工作过程:

  • 服务器通过 ServerSocket 对象监听 8080 端口,等待客户端连接。
  • 每当有一个客户端连接到服务器时,serverSocket.accept() 会阻塞当前线程,直到有客户端连接进来。
  • 然后,服务器会为每个客户端连接创建一个新线程来处理该客户端的请求。
  • 线程通过 BufferedInputStream 读取客户端发送的数据,并通过 BufferedOutputStream 回复客户端。

3.2 客户端

public class BioClient {public static void start() throws IOException {Socket socket = new Socket("127.0.0.1", 8080);String msg = "Hi,This is the BioClient";byte[] msgBytes = msg.getBytes();//1.拿到输出流//2.对输出流进行处理BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(socket.getOutputStream());//3.输出msgbufferedOutputStream.write(msgBytes);bufferedOutputStream.flush();System.out.println("客户端发送消息完毕: " + msg);System.out.println("客户端开始接收到消息==========");//4.对输入流进行处理BufferedInputStream bufferedInputStream = new BufferedInputStream(socket.getInputStream());System.out.println("客户端接收到的消息:");byte[] inBytes = new byte[1024];int len;//5.读取输入流if ((len = bufferedInputStream.read(inBytes)) != -1) {String result = new String(inBytes, 0, len);System.out.println(result);}//6.关闭输入输出流bufferedOutputStream.close();bufferedInputStream.close();socket.close();}public static void main(String[] args) throws IOException {start();}
}

工作过程:

  • 与服务端建立连接
  • 发送消息给服务端
  • 接收服务端返回的消息

相关文章:

Java BIO详解

一、简介 1.1 BIO概述 BIO(Blocking I/O),即同步阻塞IO(传统IO)。 BIO 全称是 Blocking IO,同步阻塞式IO,是JDK1.4之前的传统IO模型,就是传统的 java.io 包下面的代码实现。 服务…...

Haproxy+keepalived高可用集群,haproxy宕机的解决方案

Haproxykeepalived高可用集群,允许keepalived宕机,允许后端真实服务器宕机,但是不允许haproxy宕机, 所以下面就是解决方案 keepalived配置高可用检测脚本 ,master和backup都要添加 配置脚本 # vim /etc/keepalived…...

98,【6】 buuctf web [ISITDTU 2019]EasyPHP

进入靶场 代码 <?php // 高亮显示当前 PHP 文件的源代码&#xff0c;通常用于调试或展示代码&#xff0c;方便用户查看代码逻辑 highlight_file(__FILE__);// 从 GET 请求中获取名为 _ 的参数值&#xff0c;并赋值给变量 $_ // 符号用于抑制可能出现的错误信息&#xff…...

九. Redis 持久化-RDB(详细讲解说明,一个配置一个说明分析,步步讲解到位)

九. Redis 持久化-RDB(详细讲解说明&#xff0c;一个配置一个说明分析&#xff0c;步步讲解到位) 文章目录 九. Redis 持久化-RDB(详细讲解说明&#xff0c;一个配置一个说明分析&#xff0c;步步讲解到位)1. RDB 概述2. RDB 持久化执行流程3. RDB 的详细配置4. RDB 备份&恢…...

小程序越来越智能化,作为设计师要如何进行创新设计

一、用户体验至上 &#xff08;一&#xff09;简洁高效的界面设计 小程序的特点之一是轻便快捷&#xff0c;用户期望能够在最短的时间内找到所需功能并完成操作。因此&#xff0c;设计师应致力于打造简洁高效的界面。避免过多的装饰元素和复杂的布局&#xff0c;采用清晰的导航…...

(done) MIT6.S081 2023 学习笔记 (Day7: LAB6 Multithreading)

网页&#xff1a;https://pdos.csail.mit.edu/6.S081/2023/labs/thread.html (任务1教会了你如何用 C 语言调用汇编&#xff0c;编译后链接即可) 任务1&#xff1a;Uthread: switching between threads (完成) 在这个练习中&#xff0c;你将设计一个用户级线程系统中的上下文切…...

C++泛型编程指南09 类模板实现和使用友元

文章目录 第2章 类模板 Stack 的实现2.1 类模板 Stack 的实现 (Implementation of Class Template Stack)2.1.1 声明类模板 (Declaration of Class Templates)2.1.2 成员函数实现 (Implementation of Member Functions) 2.2 使用类模板 Stack脚注改进后的叙述总结脚注2.3 类模板…...

PHP Composer:高效依赖管理工具详解

PHP Composer:高效依赖管理工具详解 引言 在PHP开发领域,依赖管理是项目构建过程中的重要环节。Composer的出现,极大地简化了PHP项目的依赖管理,使得开发者可以更加高效地构建和维护PHP应用程序。本文将深入探讨PHP Composer的使用方法、功能特点以及它在项目开发中的应用…...

JVM执行流程与架构(对应不同版本JDK)

直接上图&#xff08;对应JDK8以及以后的HotSpot&#xff09; 这里主要区分说明一下 方法区于 字符串常量池 的位置更迭&#xff1a; 方法区 JDK7 以及之前的版本将方法区存放在堆区域中的 永久代空间&#xff0c;堆的大小由虚拟机参数来控制。 JDK8 以及之后的版本将方法…...

C# 精炼题18道题(类,三木运算,Switch,计算器)

1.数组元素和 2.数组元素乘积 3.数组元素平均数 4.数组中最大值 5.数组中的偶数 6.数组中的阶乘 7.数组反转 8.字符串反转 9.回文字符串 10.检查回文 11.最小最大值 12.找素数 13.字符串中的最长无重复字符串 14.字符串去重 15.数组中计算两数之和 16.数字到字符…...

利用matlab寻找矩阵中最大值及其位置

目录 一、问题描述1.1 max函数用法1.2 MATLAB中 : : :的作用1.3 ind2sub函数用法 二、实现方法2.1 方法一&#xff1a;max和find2.2 方法二&#xff1a;max和ind2sub2.3 方法对比 三、参考文献 一、问题描述 matlab中求最大值可使用函数max&#xff0c;对于一维向量&#xff0…...

基于开源AI智能名片2 + 1链动模式S2B2C商城小程序视角下的个人IP人设构建研究

摘要&#xff1a;本文深入探讨在开源AI智能名片2 1链动模式S2B2C商城小程序的应用场景下&#xff0c;个人IP人设构建的理论与实践。通过剖析个人IP人设定义中的“诉求”“特质”“可感知”三要素&#xff0c;结合该小程序特点&#xff0c;阐述其对个人IP打造的影响与推动作用&…...

刷题汇总一览

文章目录 贪心动态规划数据结构滑动窗口与双指针前缀和动态规划 本题单设计力扣、牛客等多个刷题网站 贪心 贪心后悔 徒步旅行中的补给问题 LCP 30.魔塔游戏 题目使用到的思想解题分析徒步旅行中的补给问题每次我们都加入当前补给点的k个选择&#xff0c;同时进行升序排序&am…...

Java中的常见对象类型解析

在Java开发中&#xff0c;数据的组织和传递是一个重要的概念。为了确保代码的清晰性、可维护性和可扩展性&#xff0c;我们通常会根据不同的用途&#xff0c;设计和使用不同类型的对象。这些对象的作用各不相同&#xff0c;但它们共同为构建高效、模块化的软件架构提供支持。 …...

Jupyter Lab的使用

Lab与Notebook的区别: Jupyter Lab和Jupyter notebook有什么区别&#xff0c;这里找到一篇博客不过我没细看&#xff0c; Jupyter Lab和Jupyter Notebook的区别 - codersgl - 博客园 使用起来Lab就是一个更齐全、功能更高级的notebook&#xff0c; 启用滚动输出: 有时候一个…...

SpringBoot中关于knife4j 中的一些相关注解

1、效果图 对比可以明显的看到加了注解与没有加注解所表现出来的效果不同&#xff08;加了注解的更加明了清晰&#xff09; 2、实现效果 Tag注解‌用于为测试方法或测试类添加标签&#xff0c;以便在执行测试时根据标签进行过滤。使用Tag注解可以更灵活地控制测试的执行&#…...

【C++】static关键字

在 C 中&#xff0c;static 关键字有多种用途&#xff0c;主要用于控制变量和函数的存储期、作用域和链接性。以下是对 static 关键字的详细介绍&#xff0c;包括其不同用法和示例。 1. 静态局部变量 定义&#xff1a;在函数内部声明的静态变量&#xff0c;其生命周期从程序开…...

内核定时器3-用户空间定时器

用户空间定时器与内核定时器的关系 虽然用户空间定时器和内核定时器在实现上各自独立&#xff0c;但用户空间定时器通常依赖于内核定时器提供的基础设施。以下是具体关系&#xff1a; 依赖性 用户空间定时器的实现基于内核定时器。 例如&#xff0c;POSIX 定时器使用内核的 h…...

知识管理系统助力企业信息共享与创新思维的全面提升研究

内容概要 知识管理系统的引入极大地改变了企业内部的信息流程与创新机制。通过有效整合与管理组织内的知识资源&#xff0c;这些系统不仅降低了信息孤岛的现象&#xff0c;还提升了员工之间的协作能力。企业在信息共享方面&#xff0c;通过知识管理系统构建了一个透明、高效的…...

LLM - 基于LM Studio本地部署DeepSeek-R1的蒸馏量化模型

文章目录 前言开发环境快速开始LM Studio简单设置模型下载开始对话 模型选择常见错误最后 前言 目前&#xff0c;受限于设备性能&#xff0c;在本地部署的基本都是DeepSeek-R1的蒸馏量化模型&#xff0c;这些蒸馏量化模型的表现可能并没有你想象的那么好。绝大部分人并不需要本…...

本地部署 DeepSeek-R1:简单易上手,AI 随时可用!

&#x1f3af; 先看看本地部署的运行效果 为了测试本地部署的 DeepSeek-R1 是否真的够强&#xff0c;我们随便问了一道经典的“鸡兔同笼”问题&#xff0c;考察它的推理能力。 &#x1f4cc; 问题示例&#xff1a; 笼子里有鸡和兔&#xff0c;总共有 35 只头&#xff0c;94 只…...

实现网站内容快速被搜索引擎收录的方法

本文转自&#xff1a;百万收录网 原文链接&#xff1a;https://www.baiwanshoulu.com/6.html 实现网站内容快速被搜索引擎收录&#xff0c;是网站运营和推广的重要目标之一。以下是一些有效的方法&#xff0c;可以帮助网站内容更快地被搜索引擎发现和收录&#xff1a; 一、确…...

浅谈量化感知训练(QAT)

1. 为什么要量化&#xff1f; 假设你训练了一个神经网络模型&#xff08;比如人脸识别&#xff09;&#xff0c;效果很好&#xff0c;但模型太大&#xff08;比如500MB&#xff09;&#xff0c;手机根本跑不动。于是你想压缩模型&#xff0c;让它变小、变快。 最直接的压缩方法…...

对象的实例化、内存布局与访问定位

一、创建对象的方式 二、创建对象的步骤: 一、判断对象对应的类是否加载、链接、初始化: 虚拟机遇到一条new指令&#xff0c;首先去检查这个指令的参数能否在Metaspace的常量池中定位到一个类的符号引用&#xff0c;并且检查这个符号引用代表的类是否已经被加载、解析和初始化…...

制造业设备状态监控与生产优化实战:基于SQL的序列分析与状态机建模

目录 1. 背景与挑战 2. 数据建模与采集 2.1 数据表设计 设备状态表(记录设备实时状态变更)...

OpenAI推出Deep Research带给我们怎样的启示

OpenAI 又发新产品了&#xff0c;这次是面向深度研究领域的智能体产品 ——「Deep Research」&#xff0c;貌似被逼无奈的节奏… 在技术方面&#xff0c;Deep Research搭载了优化后o3模型并通过端到端强化学习在多个领域的复杂浏览和推理任务上进行了训练。因没有更多的技术暴露…...

K8S学习笔记-------1.安装部署K8S集群环境

1.修改为root权限 #sudo su 2.修改主机名 #hostnamectl set-hostname k8s-master01 3.查看网络地址 sudo nano /etc/netplan/01-netcfg.yaml4.使网络配置修改生效 sudo netplan apply5.修改UUID&#xff08;某些虚拟机系统&#xff0c;需要设置才能生成UUID&#xff09;#…...

【基于SprintBoot+Mybatis+Mysql】电脑商城项目之用户登录

&#x1f9f8;安清h&#xff1a;个人主页 &#x1f3a5;个人专栏&#xff1a;【Spring篇】【计算机网络】【Mybatis篇】 &#x1f6a6;作者简介&#xff1a;一个有趣爱睡觉的intp&#xff0c;期待和更多人分享自己所学知识的真诚大学生。 目录 &#x1f3af;1.登录-持久层 &…...

【Deep Seek本地化部署】模型实测:规划求解python代码

目录 前言 一、实测 1、整数规划问题 2、非线性规划问题 二、代码正确性验证 1、整数规划问题代码验证 2、非线性规划问题代码验证 三、结果正确性验证 1、整数规划问题结果正确性验证 2、非线性规划问题正确性验证 四、整数规划问题示例 后记 前言 模型&#xff…...

虚幻基础17:动画蓝图

能帮到你的话&#xff0c;就给个赞吧 &#x1f618; 文章目录 animation blueprint图表&#xff08;Graph&#xff09;&#xff1a; 编辑动画逻辑。变量&#xff08;Variables&#xff09;&#xff1a; 管理动画参数。函数&#xff08;Functions&#xff09;&#xff1a; 自定义…...