首页 > 编程语言 >Java的I/O模型

Java的I/O模型

时间:2024-06-17 10:02:05浏览次数:27  
标签:异步 Java NIO 队列 模型 阻塞 线程 客户端

Java的I/O发展简史

从JDK1.0到JDK1.3,Java的I/O类库都非常原始,很多UNIX网络编程中的概念或者接口在I/O类库中都没有体现,比如Pipe、Channel、Buffer和Selector等。

2002年发布JDK1.4时,NIO以JSR-51的身份正式随JDK发布。它新增加了java.nio包,提供了很多进行异步I/O开发的API和类库。

新的NIO类库的提供,促进了基于Java的异步非阻塞编程的发展和应用,但是,它对文件系统的处理能力仍显不足,主要问题如下:

  • 没有统一的文件属性(例如读写权限)
  • API能力比较弱,例如目录的级联创建和递归遍历,往往需要自己实现
  • 底层存储系统的一些高级API无法使用
  • 所有的文件操作都是同步阻塞调用,不支持异步文件读写操作

2011年7月28日,JDK1.7正式发布。将原来的NIO类库进行了升级,被称为NIO2.0。NIO2.0由JSR-203演进而来,主要提供了三个方面的改进:

  • 提供批量获取文件属性的API,具有平台无关性,不与特性的文件系统耦合,提供了标准文件系统的SPI,供各个服务提供商扩展实现
  • 提供了AIO功能,支持基于文件的异步I/O操作和针对网络套接字的异步操作
  • 完成JSR-51定义的通道功能,包含对配置和多播数据报等的支持

Java的I/O模型

同步阻塞式I/O(BIO)

采用BIO通信模型的服务端,通常由一个独立的Acceptor线程负责监听客户端的连接,它接收到客户端连接请求之后为每个客户端创建一个新的线程进行链路处理,处理完成之后,通过输出流返回应答给客户端,线程销毁。这就是典型的一请求一应答通信模型。

这种模型,缺乏弹性伸缩能力,客户端并发访问量增加后,服务端线程个数与客户端并发数呈1:1,线程膨胀后,系统性能急剧下降,并发量增大可能导致线程堆栈溢出、创建新线程失败等问题,最终导致进程宕机或者僵死,不能对外提供服务。

为了解决BIO的问题,通过线程池和任务队列,优化出来一种伪异步I/O的模型。

当有新的客户端接入的时候,将客户端的Socket封装成一个Task(该任务实现java.lang.Runnable接口)投递到后端的线程池中进行处理,JDK的线程池维护一个消息队列和N个活跃线程对消息队列中的任务进行处理。由于线程池可以设置消息队列的大小和最大线程数,因此,它的资源占用是可控的,无论多少个客户端并发访问,都不会导致资源的耗尽和宕机。

伪异步I/O实际上仅仅只是对之前I/O线程模型的一个简单优化,它无法从根本上解决同步I/O导致的通信线程阻塞问题。如果通信对方返回应答时间过长,会引起的级联故障。

  1. 服务端处理缓慢,返回应答消息耗费60s,平时只需要10ms
  2. 采用伪异步I/O的线程正在读取故障服务节点的响应,由于读取输入流是阻塞的,因此,它将会被同步阻塞60s
  3. 假如所有的可用线程都被故障服务器阻塞,那后续所有的I/O消息都将在队列中排队
  4. 由于线程池采用阻塞队列实现,当队列积满之后,后续入队列的操作将被阻塞
  5. 由于前端只有一个Accptor线程接收客户端接入,它被阻塞在线程池的同步阻塞队列之后,新的客户端请求消息将被拒绝,客户端会发生大量的连接超时
  6. 由于几乎所有的连接都超时,调用者会认为系统已经崩溃,无法接收新的请求消息

同步非阻塞式I/O(NIO)

NIO的包含的一些概念:

  • 进行异步I/O操作的缓冲区ByteBuffer等
  • 进行异步I/O操作的管道Pipe
  • 进行各种I/O操作(异步或者同步)的Channel,包括ServerSocketChannel和SocketChannel
  • 多种字符集的编码能力和解码能力
  • 实现非阻塞I/O操作的多路复用器Selector
  • 基于流行的Perl实现的正则表达式类库
  • 文件通道FileChannel

NIO服务端序列图

NIO客户端序列图

NIO代码比较复杂,使用NIO编程的优点如下:

  • 客户端发起的连接操作是异步的,可以通过在多路复用器注册OP_CONNECT等待后续结果,不需要像之前的客户端那样被同步阻塞。
  • SocketChannel的读写操作都是异步的,如果没有可读写的数据它不会同步等待,直接返回,这样I/O通信线程就可以处理其他的链路,不需要同步等待这个链路可用
  • 线程模型的优化:由于JDK的Selector在Linux等主流操作系统上通过epoll实现,它没有连接句柄数的限制(只受限于操作系统的最大句柄数或者对单个进程的句柄限制),这意味着一个Selector线程可以同时处理成千上万个客户端连接,而且性能不会随着客户端的增加而线性下降,因此,它非常适合做高性能、高负载的网络服务器

异步非阻塞式I/O(AIO)

NIO2.0引入了新的异步通道的概念,并提供了异步文件通道和异步套接字通道的实现。异步通道提供两种方式获取获取操作结果。

  •  通过java.util.concurrent.Future类来表示异步操作的结果;
  • ◎在执行异步操作的时候传入一个java.nio.channels。

CompletionHandler接口的实现类作为操作完成的回调。
NIO2.0的异步套接字通道是真正的异步非阻塞I/O,它对应UNIX网络编程中的事件驱动I/O(AIO),它不需要通过多路复用器(Selector)对注册的通道进行轮询操作即可实现异步读写,从而简化了NIO的编程模型。

总结

不同的I/O模型由于线程模型、API等差别很大,所以用法的差异也非常大。几种I/O模型的功能和特性对比如下表所示:

标签:异步,Java,NIO,队列,模型,阻塞,线程,客户端
From: https://blog.csdn.net/qq_34149443/article/details/139706233

相关文章

  • 通过ModelScope开源Embedding模型将图片转换为向量
    本文介绍如何通过ModelScope魔搭社区中的视觉表征模型将图片转换为向量,并入库至向量检索服务DashVector中进行向量检索。ModelScope魔搭社区旨在打造下一代开源的模型即服务共享平台,为泛AI开发者提供灵活、易用、低成本的一站式模型服务产品,让模型应用更简单。ModelScope魔搭......
  • 浅拷贝、深拷贝与序列化【初级Java必需理解的概念】
    浅拷贝首先创建两个类,方便理解浅拷贝@DataclassStudentimplementsCloneable{//年龄和名字是基本属性privateintage;privateStringname;//书包是引用属性privateBagbag;publicStudent(intage,Stringname,Bagbag){this.......
  • Java 6.16 DeepClone and ShallowClone
    浅克隆:复制对象的引用地址,导致克隆对象和原始对象共享引用类型字段的实际对象。classPersonimplementsCloneable{Stringname;Addressaddress;publicPerson(Stringname,Addressaddress){this.name=name;this.address=add......
  • Java - function
     Java-Assignment04(100pts)InstructionsWriteeachexerciseinitsownmethod.Uncommentthefunctioncallsinmain()toactivateeachexercise.Referto"Assignment01"ifnecessary.Foreachexercise,usecommentstowritepseudocodew......
  • 面试官:Java中缓冲流真的性能很好吗?我看未必
    一、写在开头上一篇文章中,我们介绍了JavaIO流中的4个基类:InputStream、OutputStream、Reader、Writer,那么这一篇中,我们将以四个基类所衍生出来,应对不同场景的数据流进行学习。二、衍生数据流分类我们上面说了java.io包中有40多个类,都从InputStream、OutputStream、Reader、Wr......
  • 第二章JAVA的第一个开发程序
    1.JAVA的源文件以:.java结尾2.class:类,代码容器class类名Hello{  publicstaticvoidmain(String[]args){  System.out.println("Hello,world");  }}(1)main函数:被称为主函数,程序的入口,一个类中最多只能有一个主函数(2)System.out.println();//控制台打印出......
  • 5 个 JavaScript 自定义的实用函数
    嘿!......
  • 【跌倒检测】基于隐马尔可夫模型HMM和支持向量机SVM实现形状特征跌倒检测 附Matlab代
     ✅作者简介:热爱科研的Matlab仿真开发者,修心和技术同步精进,代码获取、论文复现及科研仿真合作可私信。......
  • Java高手的30k之路|面试宝典|精通Map篇
    HashMapHashMap是Java集合框架中非常重要的一部分,它是基于哈希表的数据结构。1.基于哈希表的实现HashMap基于哈希表实现。哈希表是通过将键(Key)映射到值(Value)的一种数据结构。具体来说,HashMap使用一个数组和链表(在冲突较少时)或红黑树(在冲突较多时)来存储元素。2.负......
  • 【Java基础】输入输出流(IO流)
     目录一、流的概念二、输入输出流的类层次结构图三、使用 InputStream 和 OutputStream流类四、使用Reader和Writer流类Java语言的输入输出功能必须借助于输入输出包java.io来实现,Java开发环境提供了丰富的流类,完成从基本的输人输出到文件操作。利用java.io......