作者:孙玉昌,昵称【一一哥】,另外【壹壹哥】也是我哦
千锋教育高级教研员、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模型还存在以下缺点:
线程开销大。每个连接都需要一个独立的线程来处理,当连接数量增加时,线程数量也会增加,线程切换会带来较大的开销。 同步阻塞IO导致IO效率低下。当数据量较大时,每个连接的IO操作都是同步阻塞的,如果读写操作耗时较长,就会阻塞线程,从而导致IO效率低下。 可靠性较差。在客户端请求频繁,服务端响应慢的情况下,会出现大量的超时现象,而超时会导致客户端关闭连接,服务端也会出现一些不稳定的现象。 无法实现真正的高并发。由于BIO模型的限制,无法实现真正的高并发,无法满足现代互联网高并发的需求。
3. 使用场景
虽然BIO模型在高并发情景下存在很多缺点,但也不是一无是处。BIO模型编程简单,易于理解和实现,适用于连接数较少且对并发要求不高的场景,所以现在开发时仍然很常用,比如:
- 连接数比较少且响应时间要求不高的场景。例如一些内部管理系统,连接数较少,响应时间要求不高,使用BIO模型可以减少系统的复杂性;
- 文件传输场景:因文件传输过程中的数据量较大,使用BIO模型可以避免因为内存占用过高而导致的系统崩溃问题;
- 安全控制场景:BIO模型中的每个连接都需要独立的线程,因此可以更好地控制每个连接的权限和安全性,避免出现安全漏洞。
总之,BIO模型虽然已经过时,但在某些特定的场景下,仍然可以发挥其优势,因此了解BIO模型的原理和应用场景,对我们来说仍然是有价值的。
4. BIO基本概念
在BIO模型中,通常会有以下几个概念:
- 服务端:提供服务的主机;
- 客户端:访问服务的主机;
- 端口:服务的入口,用于区分不同的服务;
- Socket:客户端和服务端之间的连接,包含了通信双方的IP地址和端口号;
- ServerSocket:服务端监听特定端口的对象,用于等待客户端的连接请求。
接下来壹哥会利用这几个概念,带大家实现一个客户端与服务端通信的案例,我们继续往下看。
5. 代码案例
接下来,壹哥会设计一个Socket客户端与ServerSocket服务端通信的案例,来给大家演示BIO模型的基本实现。
5.1 服务端代码
壹哥首先创建一个ServerSocket服务端的代码案例,在这个例子中,我们将创建一个简单的服务端程序,它监听9999端口,等待客户端的连接请求,并能够接收客户端发送的消息。
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客户端的代码案例,如下所示:
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模型,欢迎大家继续关注哦。另外如果你独自学习觉得有很多困难,可以加入壹哥的学习互助群,大家一起交流学习。