image.png
传统的同步阻塞模型开发中,ServerSocket 负责绑定 IP 地址,启动监听端口;Socket 负 责发起连接操作。连接成功后,双方通过输入和输出流进行同步阻塞式通信。
传统 BIO 通信模型:采用 BIO 通信模型的服务端,通常由一个独立的 Acceptor 线程负 责监听客户端的连接,它接收到客户端连接请求之后为每个客户端创建一个新的线程进行链 路处理,处理完成后,通过输出流返回应答给客户端,线程销毁。即典型的一请求一应答模 型,同时数据的读取写入也必须阻塞在一个线程内等待其完成。
该模型最大的问题就是缺乏弹性伸缩能力,当客户端并发访问量增加后,服务端的线程 个数和客户端并发访问数呈 1:1 的正比关系,Java 中的线程也是比较宝贵的系统资源,线程 数量快速膨胀后,系统的性能将急剧下降,随着访问量的继续增大,系统最终就死-掉-了。
为了改进这种一连接一线程的模型,我们可以使用线程池来管理这些线程,实现 1 个或 多个线程处理 N 个客户端的模型(但是底层还是使用的同步阻塞 I/O),通常被称为“伪异 步 I/O 模型“。
我们知道,如果使用 CachedThreadPool 线程池(不限制线程数量,如果不清楚请参考 文首提供的文章),其实除了能自动帮我们管理线程(复用),看起来也就像是 1:1 的客户 端:线程数模型,而使用 FixedThreadPool 我们就有效的控制了线程的最大数量,保证了系 统有限的资源的控制,实现了 N:M 的伪异步 I/O 模型。 但是,正因为限制了线程数量,如果发生读取数据较慢时(比如数据量大、网络传 输慢等),大量并发的情况下,其他接入的消息,只能一直等待,这就是最大的弊端。

对 BIO 编程流程的梳理

1) 服务器端启动一个 ServerSocket
2) 客户端启动 Socket 对服务器进行通信,默认情况下服务器端需要对每个客户 建立一个线程与之通讯
3) 客户端发出请求后, 先咨询服务器是否有线程响应,如果没有则会等待,或者被拒绝
4) 如果有响应,客户端线程会等待请求结束后,在继续执行
image.jpeg

Java BIO 问题分析

1) 每个请求都需要创建独立的线程,与对应的客户端进行数据 Read,业务处理,数据 Write 。
2) 当并发数较大时,需要创建大量线程来处理连接,系统资源占用较大。
3) 连接建立后,如果当前线程暂时没有数据可读,则线程就阻塞在 Read 操作上,造成线程资源浪费