Java IO框架之BIONIOAIO
Posted 傲骄鹿先生
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java IO框架之BIONIOAIO相关的知识,希望对你有一定的参考价值。
我是傲骄鹿先生,沉淀、学习、分享、成长。
如果你觉得文章内容还可以的话,希望不吝您的「一键三连」,文章里面有不足的地方希望各位在评论区补充疑惑、见解以及面试中遇到的奇葩问法
目录
一、概述
I/O 模型简单理解为:就是使用什么样的通道进行数据的发送和接收,很大程度上决定了程序通信的性能。Java 支持 3 种网络编程模型:BIO、NIO、AIO。
Java BIO:同步并阻塞
(传统阻塞型),服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不作任何事情会造成不必要的线程开销。
Java NIO:同步非阻塞
,服务器实现模式为一个线程处理多个请求(连接),即客户端发送的连接请求会被注册到多路复用器上,多路复用器轮询到有 I/O 请求就会进行处理。
Java AIO:异步非阻塞
,AIO 引入了异步通道的概念,采用了 Proactor 模式,简化了程序编写,有效的请求才启动线程,它的特点是先由操作系统完成后才通知服务端程序启动线程去处理,一般适用于连接数较多且连接时间较长的应用。
我们可以简单通俗的进行如下的理解:
同步阻塞
:你到饭馆点餐,然后在那等着,还要一边喊:好了没啊!同步非阻塞
:在饭馆点完餐,就去遛狗了。不过溜一会儿,就回饭馆喊一声:好了没啊!异步阻塞
:遛狗的时候,接到饭馆电话,说饭做好了,让您亲自去拿。异步非阻塞
:饭馆打电话说,我们知道您的位置,一会给你送过来,安心遛狗就可以了。
二、BIO、NIO、AIO 使用场景分析
- BIO 方式适用于
连接数比较小且固定
的架构,这种方式对服务器资源要求比较高,并发局限于应用中,JDK1.4 之前唯一的选择,程序较为简单容易理解。 - NIO 方式适用于
连接数目多且连接比较短
的架构,比如聊天服务器,弹幕系统,服务器间通讯等,编程比较复杂,JDK1.4 开始支持。 - AIO 方式适用于
连接数目多且连接比较长
的架构,比如相册服务器,充分调用 OS 参与并发操作,变成比较复杂,JDK7 开始支持。
三、BIO(同步阻塞
)
Java BIO 就是传统的 Java IO 编程,其相关的类和接口在 java.io 包下。
BIO(Blocking I/O):同步阻塞
,服务器实现模式为一个连接一个线程,即客户端有连接请求时,服务器就会需要启动一个线程来进行处理。如果这个连接不作任何事情就会造成不必要的开销,可以通过线程池机制改善。
1、简要流程
- 服务器驱动一个 ServerSocket。
- 客户端启动 Socket 对服务器进行通信,默认情况下服务器端需要对每一个客户端建立一个线程进行通信。
- 客户端发出请求后,先咨询服务器时候否线程响应,如果没有则会等待,或者被拒绝。
- 如果有响应,客户端线程会等待请求结束后,再继续执行。
2、服务端代码案例
public class Server
public static void main(String[] args) throws IOException
//创建线程池
ExecutorService executorService = Executors.newCachedThreadPool();
//创建serverSocket
ServerSocket serverSocket = new ServerSocket(6666);
for (; ; )
System.out.println("等待连接中...");
//监听,等待客户端连接
Socket socket = serverSocket.accept();
System.out.println("连接到一个客户端");
executorService.execute(() -> handler(socket));
//编写一个handler方法,和客户端通讯
public static void handler(Socket socket)
byte[] bytes = new byte[1024];
System.out.println("当前线程信息: " + Thread.currentThread().getName());
try
//通过socket获取输入流
InputStream inputStream = socket.getInputStream();
//循环读取客户端发送的数据
while (inputStream.read(bytes) != -1)
System.out.println(Thread.currentThread().getName()+ " : 发送信息为 :"+ new String(bytes, 0, bytes.length));
catch (IOException e)
e.printStackTrace();
finally
System.out.println("关闭连接");
try
socket.close();
catch (IOException e)
e.printStackTrace();
3、问题分析
- 每个请求都需要创建独立的线程,与对应的客户端进行数据处理。
- 当并发数大时,需要
创建大量线程来处理连接
,系统资源占用较大。 - 连接建立后,如果当前线程暂时没有数据可读,则当前线程会一直阻塞在 Read 操作上,造成线程资源浪费。
四、NIO(同步非阻塞
)
Java NIO 全称 Java non-blocking IO,指的是 JDK 提供的新 API。从 JDK 1.4 开始,Java 提供了一系列改进的输入/输出的新特性,被统称为 NIO,即 New IO,是同步非阻塞
的。
NIO 相关类都放在 java.nio 包下,并对原 java.io 包中很多类进行了改写。
NIO 有三大核心部分:Channel(管道)
、Buffer(缓冲区)
、Selector(选择器)
。
NIO 是面向缓冲区
编程的。数据读取到了一个它稍微处理的缓冲区,需要时可在缓冲区中前后移动,这就增加了处理过程中的灵活性,使用它可以提供非阻塞的高伸缩性网络。
Java NIO 的非阻塞模式,使一个线程从某通道发送请求读取数据,但是它仅能得到目前可用数据,如果目前没有可用数据时,则说明都不会获取,而不是保持线程阻塞,所以直到数据变为可以读取之前,该线程可以做其他事情。非阻塞写入同理。
1、NIO Buffer 的基本使用
public class BufferTest
public static void main(String[] args)
//同理对应的还有:ByteBuffer,IntBuffer,FloatBuffer,CharBuffer,ShortBuffer,DoubleBuffer,LongBuffer
//创建一个Buffer,大小为5
IntBuffer buffer = IntBuffer.allocate(5);
//存放数据
for (int i = 0; i < buffer.capacity(); i++)
buffer.put(i);
//切换成读模式. 读写切换
buffer.flip();
while (buffer.hasRemaining())
System.out.println(buffer.get()); // 0 1 2 3 4
2、NIO 和 BIO 对比
- BIO 以流的方式处理数据,而 NIO 以块的方式处理数据,块 I/O 的效率比流 I/O 高很多。
- BIO 是阻塞的,而 NIO 是非阻塞的。
- BIO 基于字节流和字符流进行操作,而 NIO 基于 Channel(通道)和 Buffer(缓冲区)进行操作,数据总是从通道读取到缓冲区中,或者从缓冲区写入到通道中。Selector(选择器)用于监听多个通道事件(比如连接请求,数据到达等),因此
使用单个线程就可以监听多个客户端通道
。
三、NIO三大核心组件关系
- 每个 Channel 对应一个 Buffer。
- Selector 对应一个线程,一个线程对应多个 Channel。
- 该图反应了有三个 Channel 注册到该 Selector。
- 程序切换到那个 Channel 是由
事件
决定的(Event)。 - Selector 会根据不同的事件,在各个通道上切换。
- Buffer 就是一个内存块,底层是有一个数组。
- 数据的读取和写入是通过 Buffer,但是需要
flip()
切换读写模式。而 BIO 是单向的,要么输入流要么输出流。
关于Selector、Channel和Buffer详细内容
五、AIO
JDK 7 引入了 Asynchronous I/O,即 AIO。在进行 I/O 编程中,通常用到两种模式:Reactor 和 Proactor 。Java 的 NIO 就是 Reactor,当有事件触发时,服务器端得到通知,进行相应的处理。
AIO 叫做异步非阻塞
的 I/O,引入了异步通道的概念,采用了 Proactor 模式,简化了程序编写,有效的请求才会启动线程,特点就是先由操作系统完成后才通知服务端程序启动线程去处理,一般用于连接数较多且连接时长较长的应用。
Reactor 与 Proactor
- 两种 IO 多路复用方案:Reactor and Proactor。
- Reactor 模式是基于同步 I/O 的,而 Proactor 模式是和异步 I/O 相关的。
由于 AIO 目前应用并不广泛,所以本文只是讲述 AIO 基本介绍。
六、BIO、NIO、AIO的对比
BIO g (Blocking I/O):同步阻塞 I/O 模式,数据的读取写入必须阻塞在一个线程内等待
其完成。在活动连接数不是特别高(小于单机 1000)的情况下,这种模型是比较不错的,可
以让每一个连接专注于自己的 I/O 并且编程模型简单,也不用过多考虑系统的过载、限流等问
题。线程池本身就是一个天然的漏斗,可以缓冲一些系统处理不了的连接或请求。但是,当面
对十万甚至百万级连接的时候,传统的 BIO 模型是无能为力的。因此,我们需要一种更高效
的 I/O 处理模型来应对更高的并发量。
NIO (New I/O): NIO 是一种同步非阻塞的 I/O 模型,在 Java 1.4 中引入了 NIO框架,对应 java.nio 包,提供了 Channel , Selector,Buffer 等抽象。NIO 中的 N 可以理解为 Non-blocking,不单纯是 New。它支持面向缓冲的,基于通道的 I/O 操作方法。NIO提供了与传统 BIO 模型中的 Socket 和 ServerSocket 相对应 de 的 SocketChannel 和ServerSocketChannel 两种不同的套接字通道实现,两种通道都支持阻塞和非阻塞两种模式。阻塞模式使用就像传统中的支持一样,比较简单,但是性能和可靠性都不好;非阻塞模式正好与之相反。对于低负载、低并发的应用程序,可以使用同步阻塞 I/O 来提升开发速率和更好的维护性;对于高负载、高并发的(网络)应用,应使用 NIO 的非阻塞模式来开发。
AIO (Asynchronous I/O): AIO 也就是 NIO 2。在 Java 7 中引入了 NIO 的改进版 NIO 2,它是异步非阻塞的 IO 模型。异步 IO 是基于事件和回调机制实现的,也就是应用操作之后会直接返回,不会堵塞在那里,当后台处理完成,操作系统会通知相应的线程进行后续的操作。AIO 是异步 IO 的缩写,虽然 NIO 在网络操作中,提供了非阻塞的方法,但是 NIO的 IO 行为还是同步的。对于 NIO 来说,我们的业务线程是在 IO 操作准备好时,得到通知,接着就由这个线程自行进行 IO 操作,IO 操作本身是同步的。查阅网上相关资料,我发现就目前来说 AIO 的应用还不是很广泛,Netty 之前也尝试使用过 AIO,不过又放弃了。
系列文章持续更新,微信搜一搜「傲骄鹿先生 」,回复【面试】有准备的一线大厂面试资料。
以上是关于Java IO框架之BIONIOAIO的主要内容,如果未能解决你的问题,请参考以下文章
Java 网络IO编程总结(BIONIOAIO均含完整实例代码)