从同步阻塞聊到Java三种IO方式

从同步阻塞聊到Java三种IO方式

对于刚刚成为程序猿不久的人,可能常常会被以下几个概念所混淆:
同步,异步,阻塞,非阻塞?以及从这几个概念中衍生出的几个概念,同步阻塞,同步非阻塞,异步阻塞,异步非阻塞?

小编从网上查了一些资料,发现对于这些概念的解释,要么过于简单随性,以偏概全,要么过于底层,不利于初学者记忆,反而加深了混淆。

今天小编特意从网上和书籍中收集的答案进行整理,抛砖引玉,跟大家简单聊聊这几个概念。

快速理解

首先,我们要先对这几个概念有一个直观的理解,对于初学者来说,你可以这样看待这几个概念:

阻塞非阻塞 指的是在客户端

阻塞: 意味着 客户端提出一个请求以后,在得到回应之前,只能等待
非阻塞: 意味着 客户端提出一个请求以后,在得到回应之前,客户端还可以做其他事情,可以继续提其他请求

同步异步 指的是服务器端

同步:意味着 服务器接受一个请求后,在返回结果以前不能接受其他请求
异步:意味着 服务器接受一个请求后,尽管还没有返回结果,但是可以继续接受其他请求
这样的理解其实是过于以偏概全的,因为这只是消息通知场景中的解释,但是通过代入客户端,服务器端更加方便初学者理解,因此在这里,暂且先这样解释。

举个例子

小明领着女朋友去超市购物,买了很多东西,当他走到收银员那里结账的时候,小明(客户端)发出了要求结账的讯息(请求),收银员(服务器)会对他这一要求进行处理。此时有可能产生多种场景

小明傻傻地等着收银员用计算器算出所有物品的总价,并准备付款。(同步阻塞)
小明觉得自己太傻了,于是一边和女朋友聊天,一边催促收银员快点计算出总价。(同步非阻塞)
小明傻傻地等着收银员的总价结果,收银员却把计算的工作交给计算机之后就去拿袋子帮忙装东西,直到计算机上出现了总价结果,收银员才继续回来完成收款工作。(异步阻塞)
小明觉得自己太傻了,于是一边和女朋友聊天,一边催出收银员快点计算出总价,而收银员却把计算的工作交给计算机之后就去拿袋子帮忙装东西,直到计算机上出现了总价结果,收银员才继续回来完成收款工作。(异步非阻塞)
此时的同步异步,指的是收银员是否在处理收款这一请求的过程中去做了其他的事情,这也导致了收款的结果是当时告诉了小明,还是之后又进行了额外的通知。

而阻塞非阻塞,指的是小明是否在等待处理结果的过程中去做了其他的事情。

那么因此,就能得出结论:

同步和异步:关注的是被调用者是否会通过原调用通知调用者。换句话说,处理请求者是通过原调用将结果返回,还是通过其他方式将结果通知调用者。

阻塞和非阻塞:关注的是调用者是否会一直等待被调用者的通知。换句话说,发出请求者是否会在等待过程中去做别的事情。

简单的记忆方法

同步阻塞:A调用B,然后A一直等待B的返回;B执行完后通过原调用接口返回结果。
同步非阻塞:A调用B,然后A执行其他操作,隔段时间看看原调用接口是否有返回结果;B执行完后通过原调用接口返回结果。
异步阻塞:A调用B,然后A一直等待B的回调;B执行完后通过回调、状态等其他方式通知A结果。
异步非阻塞:A调用B,然后A继续做别的,不再搭理B;B执行完后通过回调、状态等其他方式通知A结果。

Java 中的BIO,NIO和AIO

在Java中,同步异步,阻塞非阻塞的概念也应用到了很多方面,比如最常见,也是面试官经常考察的就是有关Java中几种IO方式。

Java中IO的方式通常分为同步阻塞的BIO,同步非阻塞的NIO,异步非阻塞的AIO。

BIO同步阻塞

如果你还记得我们在学习程序设计语言之初,完成的socket编程,大概就会了解到BIO的基本工作原理。

一个socket连接一个处理线程,这个线程负责相关数据传输操作,每个服务器需要多个这样的处理线程,然而这种情况下,当多个socket向服务器申请建立连接时,受限于操作系统所允许的最大线程数量的限制,服务器不能提供相应数量的处理线程,没有分配到处理线程的连接就会阻塞等待,所以BIO是阻塞的。

又因为,当进行IO操作时,由Java自己本身处理IO的读写,所以是同步的。

NIO同步非阻塞

在BIO的基础上,NIO作出了改进。考虑到每一个socket连接只有在部分时间才进行了数据传输,大多数时间都是空闲的,而在空闲的时间依然要占用线程,这就造成了浪费。

当客户端的socket连接到服务器端时,不再是每个连接分配一个处理线程,而是服务器端会专门开辟一个”注册中心”统一对其进行管理。当检测到有IO事件请求发生的时候,服务器此时才启动一个处理线程对其进行处理,这种方法解决了因为线程数量的限制,导致socket接入阻塞的问题,因此是非阻塞的。

AIO异步非阻塞

在NIO中,当Java对IO请求进行处理时,可能会需要对后端资源(比如数据库连接)进行等待,并发量小的时候还好,一旦并发量增大,则也会对服务器的性能造成影响,因此,有人提出了AIO的概念。

与NIO不同的时,对于IO请求的处理,Java将其委托给了操作系统,不再阻塞等待,当操作系统完成了相应的IO处理之后,再去通知服务器,启动线程继续对结果进行处理。因此是异步的。

小结

总的来说,大家可以这样记忆:

BIO是一个连接一个线程。(连接阻塞,Java处理IO同步)

NIO是一个请求一个线程。(没有请求时,连接不占用线程,连接非阻塞,Java处理IO同步)

AIO是一个有效请求一个线程。(连接非阻塞,Java处理IO委托给操作系统,异步进行处理)