几个概念
阻塞IO 和非阻塞IO 这两个概念是程序级别的。主要描述的是程序请求操作系统IO操作后,如果IO资源没有准备好,那么程序该如何处理的问题:前者等待;后者继续执行(但是使用线程一直轮询,直到有IO资源准备好了)。
同步IO 和 异步IO,这两个概念是操作系统级别的。主要描述的是操作系统在收到程序请求IO操作后,如果IO资源没有准备好,该如何响应程序的问题:前者不响应,直到IO资源准备好以后;后者返回一个标记(好让程序和自己知道以后的数据往哪里通知),当IO资源准备好以后,再用事件机制返回给程序。
同步阻塞模式(Blocking IO)
同步阻塞IO模型是最简单的IO模型,用户线程在内核进行IO操作时如果数据没有准备号会被阻塞。
图片来源:www.masterraghu.com
伪代码表示如下:
{ // 阻塞,直到有数据 read(socket,buffer); process(buffer); }
BIO通信方式的 特点
:
- 一个线程负责连接,多线程则为每一个接入开启一个线程。
- 一个请求一个应答。
- 请求之后应答之前客户端会一直等待(阻塞)。
BIO通信方式在单线程服务器下一次只能处理一个请求,在处理完毕之前一直阻塞。因此不适用于高并发的情况。不过可以使用多线程 稍微
改进。
Java同步阻塞模式
Java中的阻塞模式BIO,就是在 java.net
包中的Socket套接字的实现,Socket套接字是TCP/UDP等传输层协议的实现。
Java同步阻塞模式编码
多线程客户端
为了测试服务端程序,可以先编写一个多线程客户端用于请求测试。
单线程服务端
因为Java中的Socket就是BIO的模式,因此我们可以很简单的编写一个BIO单线程服务端。
SocketServer.java
多线程服务端
单线程服务器,在处理请求时只能同时处理一条,也就是说如果在请求到来时发现有请求尚未处理完毕,只能等待处理,因此使用 多线程改进
服务端。
SocketServerThread.java
看起来多线程增加了服务能力,但是很明显多线程改进之后仍有以下 局限性
:
- 接收和通知处理结果的过程依旧是单线程的。
- 系统可以创建的线程数量有限。
cat/proc/sys/kernel/threads-max
可以查看可以创建的线程数量。 - 如果线程较多,CPU需要更多的时间切换,处理真正业务的时间就会变少。
- 创建线程会消耗较多资源,JVM创建一个线程都会默认分配128KB空间。
- 多线程也无法解决因为
调用底层系统
的同步IO
而决定的同步IO机制。
同步阻塞模式总结
BIO模式因为进程的阻塞挂起,不会消耗过多的CPU资源,而且开发难度低,比较适合并发量小的网络应用开发。同时很容易发现因为请求IO会阻塞进程,所以不时候并发量大的应用。如果为每一个请求分配一个线程,系统开销就会过大。
同时在Java中,使用了多线程来处理阻塞模式,也无法解决程序在 accept()
和 read()
时候的阻塞问题。因为 accept()
和 read()
的IO模式支持是基于操作系统的,如果操作系统发现没有套接字从指定的端口传送过来,那么 操作系统就会等待
。这样 accept()
和 read()
方法就会一直等待。
GitHub源码:
https://github.com/niumoo/java-toolbox