java网络编程之I/O模型(一)

本文探讨了Java网络编程中I/O模型的发展历程,从最初的BIO模型逐步演进到NIO和AIO,详细解释了每种模型的工作原理及其优缺点。同时对比了Unix下的五种I/O模型,帮助读者深入理解同步与异步、阻塞与非阻塞的概念。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1 Java网络编程之I/O模型概述

在JDK1.4推出Java NIO之前,基于Java的所有Socket通信都采用了同步阻塞模式(BIO),在这种通信方式中,由一个Accepter线程负责监听客户端连接,并负责为每一个客户端创建一个新的线程以处理客户端的请求。显而易见,当大量客户端并发连接服务器时,会造成服务端线程数的快速增加,从而影响服务器性能。为改进此模型,我们可以采用线程池的方式来代替这种每一个连接都需要新建线程的做法,我们称之为伪异步I/O模型。
由于BIO模式的种种弊端,JDK1.4版本提供了新的类库NIO,实现了Channel,Selector,Buffer,Pipe等,从而使得Java支持同步非阻塞I/O模型,在这种模型中,所有客户端的连接都注册到一个多路复用器(Selector)上,只有当客户端存在I/O请求时才启动一个线程来处理。NIO是基于缓冲区的,非阻塞式的,基于选择器的处理模型,只需要一个线程对所有的连接进行轮询,负责对准备好的数据通道进行读写操作以及对中断的连接关闭端口。这样可以大大减轻了大量并发时服务器的压力。
JDK1.7推出了新的模型AIO(异步I/O模型),当客户端发起I/O请求时,直接调用OS的read/write操作,当OS处理完成后再由负责通知服务端应用程序启动线程处理本次I/O请求。即Java将I/O实现建立在了OS自身的I/O异步机制上,因此不同OS实现机制也不一样,在window和Linux下分别是通过IOCP和Epoll实现的。
由以上可以,从BIO->NIO->AIO是依次递进的关系,BIO是对每一个连接创建一个线程,NIO是对任一连接的每一个I/O请求创建一个线程,AIO是对已经被OS接收的可以直接被应用程序处理的I/O请求创建一个线程(此时线程可以直接获取数据)

2 Unix下的五种I/O模型

要想理解java I/O的基本原理,首先可以从Unix的五种I/O模型入手,在此过程中理解同步非同步,阻塞非阻塞的概念。

下面将依次介绍这五种I/O模型

  • 阻塞 I/O
  • 非阻塞 I/O
  • I/O 复用(select 和 poll)
  • 信号驱动 I/O
  • 异步 I/O
阻塞I/O

阻塞I/O
当用户发送I/O请求时,用户线程将阻塞在recvfrom,直到内核将从网络或本地磁盘读取到相应的数据,并将其拷贝的线程内存空间,此时才返回调用结果,用户线程会一直阻塞直到调用返回结果。

非阻塞I/O

非阻塞I/O
我们可以看到,非阻塞I/O在用户发送I/O请求后若内核未准备好数据会立即返回一个数据未就绪的结果,此时用户线程不会阻塞自己,而是在周期的调用recvfrom,直到内核准备好数据,然后将数据从内核拷贝到用户空间,用户线程会周期性的调用recvfrom查询处理进度。

I/O 复用

 I/O 复用
从图中我们可以看到,用户进程首先阻塞在Selector上,Selector将通知内核获得用户进程需要的数据,当内核将某个用户进程需要的数据准备好之后将select返回可读条件,此时用户进程才调用recvfrom从内核获取数据,在数据从内核拷贝到用户内存空间时,用户进程阻塞在I/O请求上。也就是说,用户进程发起I/O请求前先注册到Selector,由Selector代替用户进程轮询,当满足可读条件时再由Selector通知用户进程发起I/O操作读取数据
使用这种方法可以用单一线程处理多个Socket监听,是第二种I/O的改良版。
在此可能有人疑问,既然I/O复用和阻塞I/O都会将线程阻塞直到返回结果,阻塞I/O会使用内核直接将结果拷贝给用户进程空间,为什么还要多此一举使用Selector呢?我的理解是,在网络环境下,当使用同步阻塞I/O时,针对每一个Socket连接服务端都需要一个线程阻塞在I/O请求上,即在概述里所说的,针对每一个客户端连接我们都会创建一个线程,此线程将负责监听该Socket并一直阻塞在I/O请求上直到成功,而Select可以使用一个线程监听多个端口,因此在内核准备数据阶段(第一阶段)不需要创建线程,数据准备好后的第二阶段才会通知服务端创建线程开始处理I/O请求(每一个I/O请求创建一个线程)。这样可以大大减少服务端并发的线程数量,从而节省资源。

信号驱动I/O

信号驱动I/O
信号驱动I/O与Select的区别在于,信号驱动由内核代替Select来通知程序处理I/O。首先用户进程会将其使用的socket注册到内核,当socket产生数据时则会使用信号的方式通知应用程序创建一个线程来处理数据。

异步I/O

异步I/O
应用程序调用I/O请求,将直接返回,内核将数据准备好后将直接将数据拷贝到用户内存空间,同时通知应用程序处理I/O,在整个过程中,应用程序可以继续执行。

3 关于 I/O模型的总结

在Unix的I/O模型中,前四个模型都是同步I/O模型,请求进程会一直阻塞直到I/O完成,只不过他们阻塞的时间段不一样而已,只有第五个为异步模型,请求进程会继续运行然后由内核将数据拷贝到用户内存空间,并通知应用程序处理结果。在Java I/O模型中,BIO方式对应于第一种I/O模型(阻塞I/O),NIO对应于第三种模型(I/O复用),AIO对应于第五种模型(异步I/O),区别在于Java是何时在服务端创建线程处理客户端的I/O请求。对于同步,我的理解是,进程在发出I/O请求后是否等待I/O返回的结果,如果需要等待返回结果才能继续运行,无论此时请求进程的状态如何,都称之为同步,否则,称之为异步。(注意:此时说的发起I/O请求后是指用户线程调用recvfrom,对于第三和第四的I/O模型来讲只包含第二阶段)。

参考资料

《Unix网络编程》
https://www.zhihu.com/question/19732473
http://www.importnew.com/21383.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值