Java BIO
目录
¶概述
Java BIO(Java Blocking IO)是传统的 Java IO 编程,其相关的类和接口在java.io
中,下面将介绍两种常见的 IO 模型分类方式
¶同步与异步
描述的是用户线程与内核的交互方式,与消息的通知机制有关:
简而言之,同步和异步两者之间的关系如下:
¶阻塞与非阻塞
阻塞和非阻塞指的是得到结果之前,当前线程能否继续执行下去,具体的细节如下:
¶BIO 通信方式简介
以前大多数网络通信方式都是阻塞模式的,即:
😣 传统 BIO 问题
¶BIO 模式下接收多个客户端
常规方式下,一个服务端只能接收一个客户端的通信请求,如果服务端需要处理很多个客户端的消息通信请求,可以使用多线程进行处理。
✨客户端每发起一个请求,服务端就创建一个新的线程来处理这个客户端的请求,这样就实现了一个客户端一个线程模型
😣 使用多线程方式处理 BIO 存在的弊端
¶多线程方式——伪异步方式
在上述案例中:客户端的并发访问增加时。服务端将呈现 1:1 的线程开销,访问量越大,系统将发生线程栈溢出,线程创建失败,最终导致进程宕机或者僵死,从而不能对外提供服务。
接下来我们采用一个伪异步 I/O 的通信框架,采用线程池和任务队列实现,当客户端接入时,将客户端的 Socket 封装成一个 Task(该任务实现 java.lang.Runnable 线程任务接口)交给后端的线程池中进行处理。JDK 的线程池维护一个消息队列和 N 个活跃的线程,对消息队列中 Socket 任务进行处理,由于线程池可以设置消息队列的大小和最大线程数,因此,它的资源占用是可控的,无论多少个客户端并发访问,都不会导致资源的耗尽和宕机。如下图:
✨采用伪异步方式特点
- 伪异步 io 采用了线程池实现,因此避免了为每个请求创建一个独立线程造成线程资源耗尽的问题,但由于底层依然是采用的同步阻塞模型,因此无法从根本上解决问题
- 如果单个消息处理的缓慢,或者服务器线程池中的全部线程都被阻塞,那么后续 socket 的 i/o 消息都将在队列中排队。新的 Socket 请求将被拒绝,客户端会发生大量连接超时
¶实现软件组播
¶深入分析
BIO 的问题关键不在于是否使用了多线程(包括线程池)处理这次请求,而在于 accept()、read()的操作点都是被阻塞
🤔 为什么accept()
、read()
方法会被阻塞?
¶总结
BIO 模型最大的缺陷是:缺乏扩展性,不能处理高性能、高并发场景,线程是 JVM 中非常宝贵的资源,当线程数膨胀后,系统的性能就会急剧下降,随着并发访问量的继续增大,系统就会出现堆栈溢创建新线程失败等问题,导致 Server 不能对外提供服务。