从零开始学Java之什么是BIO阻塞式IO模型?

作者 :孙玉昌,昵称【一一哥 】,另外【壹壹哥】也是我哦

千锋教育高级教研员、CSDN博客专家、万粉博主、阿里云专家博主、掘金优质作者

前言

经过前面的几篇文章,壹哥 就把IO流的基本用法给大家介绍完毕了。可以说,IO流是我们从零开始学Java系列以来,API最为繁多的一个知识块,所以这对初学者来说就有比较大的学习难度。而且IO流在不同的发展阶段,还经历了几大不同的分类,比如BIO、NIO和AIO。所以壹哥 会再利用几篇文章,来给大家简单说一下这几种分类的区别,毕竟这些内容在面试时也是IO流中的一个考察重点。但大家要注意,壹哥 这几篇文章,并不会特别详细地介绍NIO与AIO的内容,后面我会专门出一个NIO与AIO新技术的专栏,敬请大家持续关注壹哥哦。

------------------------------前戏已做完,精彩即开始----------------------------

全文大约【2200】 字,不说废话,只讲可以让你学到技术、明白原理的纯干货!本文带有丰富的案例及配图视频,让你更好地理解和运用文中的技术概念,并可以给你带来具有足够启迪的思考......

配套开源项目资料

Github: github.com/SunLtd/Lear...

Gitee: gitee.com/sunyiyi/Lea...

一. BIO-阻塞式IO

1. BIO简介

BIO全称为Blocking IO,即阻塞式IO ,这是Java中最传统的IO模型之一。壹哥之前带大家学习的InputStream、OutputStream、Reader、Writer等字节流和字符流都属于BIO模型。

BIO模型是最原始的一种I/O模型,它是一种阻塞式的I/O,即在读取或写入数据时会阻塞当前的线程,直到有数据可用或者写入完成。也就是说,当输入流没有数据可读时,应用程序会阻塞等待;当输出流的缓冲区已满时,应用程序也会阻塞等待。

2. BIO缺点

BIO最大的问题在于其阻塞模型。每个客户端连接都需要一个线程来处理,当多个客户端连接到服务器端时,如果有某个客户端连接阻塞在某个IO操作上,那么整个服务器端都会被阻塞,无法处理其他客户端的连接请求。尤其是当并发连接数较大时,会导致线程数量过多,占用过多的系统资源,从而影响应用程序的性能。所以这种阻塞模型在高并发访问情况下,很容易出现性能瓶颈。除此之外,BIO模型还存在以下缺点:

  1. 线程开销大。每个连接都需要一个独立的线程来处理,当连接数量增加时,线程数量也会增加,线程切换会带来较大的开销。
  2. 同步阻塞IO导致IO效率低下。当数据量较大时,每个连接的IO操作都是同步阻塞的,如果读写操作耗时较长,就会阻塞线程,从而导致IO效率低下。
  3. 可靠性较差。在客户端请求频繁,服务端响应慢的情况下,会出现大量的超时现象,而超时会导致客户端关闭连接,服务端也会出现一些不稳定的现象。
  4. 无法实现真正的高并发。由于BIO模型的限制,无法实现真正的高并发,无法满足现代互联网高并发的需求。

3. 使用场景

虽然BIO模型在高并发情景下存在很多缺点,但也不是一无是处。BIO模型编程简单,易于理解和实现,适用于连接数较少且对并发要求不高的场景,所以现在开发时仍然很常用,比如:

  • 连接数比较少且响应时间要求不高的场景。例如一些内部管理系统,连接数较少,响应时间要求不高,使用BIO模型可以减少系统的复杂性;
  • 文件传输场景:因文件传输过程中的数据量较大,使用BIO模型可以避免因为内存占用过高而导致的系统崩溃问题;
  • 安全控制场景:BIO模型中的每个连接都需要独立的线程,因此可以更好地控制每个连接的权限和安全性,避免出现安全漏洞。

总之,BIO模型虽然已经过时,但在某些特定的场景下,仍然可以发挥其优势,因此了解BIO模型的原理和应用场景,对我们来说仍然是有价值的。

4. BIO基本概念

在BIO模型中,通常会有以下几个概念:

  • 服务端:提供服务的主机;
  • 客户端:访问服务的主机;
  • 端口:服务的入口,用于区分不同的服务;
  • Socket:客户端和服务端之间的连接,包含了通信双方的IP地址和端口号;
  • ServerSocket:服务端监听特定端口的对象,用于等待客户端的连接请求。

接下来壹哥会利用这几个概念,带大家实现一个客户端与服务端通信的案例,我们继续往下看。

5. 代码案例

接下来,壹哥会设计一个Socket客户端与ServerSocket服务端通信的案例,来给大家演示BIO模型的基本实现。

5.1 服务端代码

壹哥首先创建一个ServerSocket服务端的代码案例,在这个例子中,我们将创建一个简单的服务端程序,它监听9999端口,等待客户端的连接请求,并能够接收客户端发送的消息。

java 复制代码
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * @author 一一哥Sun
 */
public class Demo14 {
    public static void main(String[] args) throws IOException {
        // 创建ServerSocket对象,监听9999端口
        try (ServerSocket serverSocket = new ServerSocket(9999)) {
            System.out.println("服务端启动成功,等待客户端连接...");
            while (true) {
                // 监听客户端的连接请求
                Socket socket = serverSocket.accept();
                System.out.println("客户端连接成功,客户端地址:" + socket.getInetAddress());
                // 读取客户端发来的消息
                InputStream inputStream = socket.getInputStream();
                // 创建BufferedReader对象
                BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
                String message = reader.readLine();
                System.out.println("客户端发来的消息:" + message);
                // 关闭资源
                reader.close();
                inputStream.close();
                socket.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

在上面的代码中,我们通过ServerSocket监听9999端口,等待客户端的连接请求。当客户端连接成功后就可以获取输入流,并使用BufferedReader读取客户端发送的消息。接着,我们将服务器的消息通过输出流发送给客户端,最后再关闭所有相关的资源。

5.2 客户端代码

接下来我们再编写一个Socket客户端的代码案例,如下所示:

java 复制代码
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.Socket;

/**
 * @author 一一哥Sun
 */
public class Demo15 {
    public static void main(String[] args) {
        try {
            // 创建客户端对象,监听9999端口
            Socket socket = new Socket("localhost", 9999);
            // 创建输出流对象
            OutputStream outputStream = socket.getOutputStream();
            BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(outputStream));
            writer.write("hello,这里是客户端");
            // 这里需要使用flush刷新,否则信息可能发不出去
            writer.flush();
            writer.close();
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

注意,壹哥在这里用到了Socket这样的API,后面我会专门再讲解Socket编程,此处大家暂时先跟着练习一下吧。

------------------------------正片已结束,来根事后烟----------------------------

二. 结语

在今天的这篇文章中,壹哥给大家梳理总结了BIO阻塞式IO模型的特性及其缺点,今天的重点内容如下:

  • 我们之前学习的字节流、字符流等都属于BIO模型;
  • BIO模型是一种阻塞式的IO流,即在读取或写入数据时会阻塞当前的线程,直到有数据可用或者写入完成;
  • BIO阻塞模型在高并发访问情况下,很容易出现性能瓶颈。

在下一篇文章中,壹哥 会给大家介绍NIO非阻塞式的IO模型,欢迎大家继续关注哦。另外如果你独自学习觉得有很多困难,可以加入壹哥的学习互助群,大家一起交流学习。

相关推荐
遇见你真好。1 分钟前
自定义注解进行数据脱敏
java·springboot
NMBG225 分钟前
[JAVAEE] 面试题(四) - 多线程下使用ArrayList涉及到的线程安全问题及解决
java·开发语言·面试·java-ee·intellij-idea
王二端茶倒水23 分钟前
大龄程序员兼职跑外卖第五周之亲身感悟
前端·后端·程序员
像污秽一样24 分钟前
Spring MVC初探
java·spring·mvc
计算机-秋大田24 分钟前
基于微信小程序的乡村研学游平台设计与实现,LW+源码+讲解
java·spring boot·微信小程序·小程序·vue
LuckyLay27 分钟前
Spring学习笔记_36——@RequestMapping
java·spring boot·笔记·spring·mapping
醉颜凉1 小时前
【NOIP提高组】潜伏者
java·c语言·开发语言·c++·算法
阿维的博客日记1 小时前
java八股-jvm入门-程序计数器,堆,元空间,虚拟机栈,本地方法栈,类加载器,双亲委派,类加载执行过程
java·jvm
qiyi.sky1 小时前
JavaWeb——Web入门(8/9)- Tomcat:基本使用(下载与安装、目录结构介绍、启动与关闭、可能出现的问题及解决方案、总结)
java·前端·笔记·学习·tomcat
lapiii3581 小时前
图论-代码随想录刷题记录[JAVA]
java·数据结构·算法·图论