Java I/O模型及其底层原理
Java I/O是Java基础之一,在面试中也比较常见,在这里我们尝试通过这篇文章阐述Java I/O的基础概念,帮助大家更好的理解Java I/O。
在刚开始学习Java I/O时,我很迷惑,因为网上绝大多数的文章都是讲解Linux网络I/O模型的,那是我总是搞不明白和Java I/O的关系。后来查了看了好多,才明白Java I/O的原理是以Linux网络I/O模型为基础的,理解了Linux网络I/O模型再学习Java I/O就很方便了,所以这篇文章,我们先来了解I/O的基本概念,再学习Linux网络I/O模型,最后再看Java中的几种I/O。
什么是I/O?
I/O是Input、Output的缩写,即对应计算机中的输入输出,以一次文件读取为例,我们需要将磁盘上的数据读取到用户空间,那么这次数据转移操作其实就是一次I/O操作,更具体的说是一次文件I/O。我们浏览网页,其中在请求一个网页时,服务器通过网络把数据发送给我们,此时程序将数据从TCP缓冲区复制到用户空间,那么这次数据转移操作其实也是一次I/O操作,更具体的说是一次网络I/O。I/O到处都在,十分重要,Java对I/O对底层操作系统的各种I/O模型进行了封装,使我们可以轻松开发。
Linux网络I/O模型
根据UNIX网络编程对I/O模型的分类,UNIX提供了5种I/O模型,分别是:阻塞I/O(Blocking I/O)、非阻塞I/O(Non-Blacking I/O)、I/O多路复用模型(I/O Multiplexing)、信号驱动式I/O(Signal Driven I/O)、异步I/O(Asynchronous I/O)。我们逐步了解一下其基本原理。
阻塞I/O(Blocking I/O)
阻塞I/O是最早最基础的I/O模型,其在读写数据过程中会阻塞。通过下图我们可以看到,当用户进程调用了recvfrom这个系统调用后,内核开始第一阶段的数据准备工作,直到内核等待数据准备完成,然后开始第二阶段的将数据从内核复制到用户空间的工作,最后内核返回结果。整个过程中用户进程都是阻塞的,直到最后返回结果后才接触阻塞block状态。阻塞I/O模型适用于并发量小且对时延不敏感的系统。
非阻塞I/O(Non-Blacking I/O)
当用户进程调用recvfrom这个系统调用后,如果内核尚未准备好数据,此时不再阻塞用户进程,而是立即返回一个EWOULDBLOCK错误。用户进程会不断发起系统调用直到内核中数据被准备好(轮询),此时将执行第二阶段的将数据从内核复制到用户空间的工作,然后内核返回结果。非阻塞I/O模型不断地轮询往往需要耗费大量cpu时间。
I/O多路复用模型(I/O Multiplexing)
I/O多路复用的优点在于单个进程可以同时处理多个网络连接的I/O,其基本原理就是select/epoll函数可以不断的轮询其负责的所有socket,当某个socket有数据到达时,就通知用户进程。
如下图所示,当用户进程调用select函数时,整个进程会被阻塞block住,但是这里的阻塞不是被socket I/O阻塞,而是被select这个函数阻塞。同时内核会监听改select负责的所有socket(这里的socket一般设置为non-blocking),当任何一个socket中的数据准备好时,select就会返回给用户进程,这时候用户进程再此发起一个系统调用,将数据从内核复制到用户空间,并返回结果。
对比I/O多路复用模型和阻塞I/O模型的流程,多路复用多了一个系统调用来完成select环节,除此之外没有太大的不同。Select的优势在于它可以同时处理多个connection,但是会多一个系统调用。多路复用本质上也不是非阻塞的。
信号驱动式I/O(Signal Driven I/O)
首先我们开启socket的信号驱动I/O功能,然后用户进程发起sigaction系统调用给内核后立即返回并可继续处理其他工作。收到sigaction系统调用的内核在将数据准备好后会按照要求产生一个signo信号通知给用户进程。然后用户进程再发起recvfrom系统调用,完成数据从内核到用户空间的复制,并返回最终结果。其基础原理图示如下:
异步I/O(Asynchronous I/O)
用户进程向内核发起系统调用后,就可以开始去做其他事情了。内核收到异步I/O的系统调用后,会直接retrun,所以这里不会对用户进程有阻塞。之后内核等待数据准备完成后会继续将数据从内核拷贝到用户空间(具体动作可以由异步I/O调用定义),然后内核回给用户进程发送一个signal,告诉用户进程I/O操作完成了,整个过程不会导致用户请求进程阻塞。
信号驱动I/O模型是内核通知我们可以发起I/O操作了,而异步I/O模式是内核告诉我们I/O操作已经完成了。
以上就是Linux的5种网络I/O模型,其中前4中都是同步I/O模型,他们真正的I/O操作环节都会将进程阻塞,只有最后一种异步I/O模型是异步I/O操作。
Java中的I/O模型
在JDK1.4之前,基于Java的所有socket通信都是使用阻塞I/O(BIO),JDK1.4提供了了非阻塞I/O(NIO)功能,不过虽然名字叫做NIO,实际底层模型是I/O多路复用,JDK1.7提供了针对异步I/O(AIO)功能。
BIO
BIO简化了上层开发,但是性能瓶颈问题严重,对高并发第时延支持差。
基于消息队列和线程池技术优化的BIO模式虽然可以对高并发支持有一定帮助,但是还是受限于线程池大小和线程池阻塞队列大小的制约,当并发数超过线程池的处理能力时,部分请求法务继续处理,会导致客户端连接超时,影响用户体验。
NIO
NIO弥补了BIO的不足,简单说就是通过selector不断轮询注册在自己上面的channel,如果channel上面有新的连接读写时间时就会被轮询出来,一个selector上面可以注册多个channel,一个线程就可以负责selector的轮询,这样就可以支持成千上万的连接。Selector就是一个轮询器,channel是一个通道,通过它来读取或者写入数据,通道是双向的,可以用于读、写、读和写。Buffer用来和channel交互,数据通过channel进出buffer。
NIO的优点是可以可靠性好以及高并发低时延,但是使用NIO的代码开发较为复杂。
AIO
AIO,或者说叫做NIO2.0,引入了异步channel的概念,提供了异步文件channel和异步socket channel的实现,开发者可以通过Future类来表示异步操作的结果,也可以在执行异步操作时传入一个channels,实现CompletionHandler接口作为回调。AIO不用开发者单独开发独立线程的selector,异步回调操作有JDK地城思安城池负责驱动,开发起来比NIO简单一些,同时保持了高可靠高并发低时延的优点。
参考:
https://blog.csdn.net/historyasamirror/article/details/5778378
https://juejin.im/post/5cce5019e51d453a506b0ebf
Java I/O模型及其底层原理的更多相关文章
- Java线程内存模型-JVM-底层原理
public class Demo1 { private static boolean initFlag=false; public static void main(String[] args) t ...
- JAVA浮点数计算精度损失底层原理与解决方案
浮点数会有精度损失这个在上大学的时候就已经被告知,但是至今完全没有想明白其中的原由,老师讲的时候也是一笔带过的,自己也没有好好琢磨.终于在工作的时候碰到了,于是google了一番. 问题: 对两个do ...
- 详解JAVA字符串类型switch的底层原理
基础 我们现在使用的Java的版本,基本上是都支持String类型的.当然除了String类型,还有int.char.byte.short.enum等等也都是支持的.然而在其底部实现中,还是基于 整型 ...
- 详解:Java字符串类型"switch"的底层原理
前言: 最近更新得会比较频繁,希望大家见谅哦! 也感谢关注我的人,我会更加更加努力去做的! 基础 我们现在使用的Java的版本,基本上是都支持String类型的.当然除了String类型,还有int. ...
- Java面试底层原理
面试发现经常有些重复的面试问题,自己也应该学会记录下来,最好自己能做成笔记,在下一次面的时候说得有条不紊,深入具体,面试官想必也很开心.以下是我个人总结,请参考: HashSet底层原理:(问了大几率 ...
- (前篇:NIO系列 推荐阅读) Java NIO 底层原理
出处: Java NIO 底层原理 目录 1.1. Java IO读写原理 1.1.1. 内核缓冲与进程缓冲区 1.1.2. java IO读写的底层流程 1.2. 四种主要的IO模型 1.3. 同步 ...
- 【转】Java 内存模型及GC原理
一个优秀Java程序员,必须了解Java内存模型.GC工作原理,以及如何优化GC的性能.与GC进行有限的交互,有一些应用程序对性能要求较高,例如嵌入式系统.实时系统等,只有全面提升内存的管理效率,才能 ...
- Java 内存模型、GC原理及算法
Java 内存模型.GC原理:https://blog.csdn.net/ithomer/article/details/6252552 GC算法:https://www.cnblogs.com/sm ...
- Java 内存模型及GC原理 (转载)
一个优秀Java程序员,必须了解Java内存模型.GC工作原理,以及如何优化GC的性能.与GC进行有限的交互,有一些应用程序对性能要求较高,例如嵌入式系统.实时系统等,只有全面提升内存的管理效率,才能 ...
随机推荐
- Django的ListView超详细用法(含分页paginate功能)
开发环境: python 3.6 django 1.11 场景一 经常有从数据库中获取一批数据,然后在前端以列表的形式展现,比如:获取到所有的用户,然后在用户列表页面展示. 解决方案 常规写法是,我们 ...
- Rabbit的字符串 字符串最小表示法
Rabbit的字符串 #include<bits/stdc++.h> using namespace std; ; char s[maxn]; int get_min_pos() { , ...
- maven开发SSH
虽然开发SSH的基本步骤都差不多,但每次都从头开始做真的会有点儿烦,把maven的SSH框的基本代码放出来,下次就可以复制粘贴哈哈. 1. 配置文件: (1)pom.xml <project x ...
- poj3613 求经过n条边的最短路 ----矩阵玩出新高度 。
For their physical fitness program, N (2 ≤ N ≤ 1,000,000) cows have decided to run a relay race usin ...
- 【JVM】GC 可达性分析中哪些算是GC ROOT?
至今为止,我基本上发现网上没有几个博客说的很明白的,今天我在这里斗胆总结一下,各位大佬,如有错误,还望指责 ^ _ ^ 首先那肯定还得是看看概念了,在JVM中,什么可以作为GC Root呢? 虚拟机栈 ...
- Elasticsearch SSL认证/证书制作
制作目的 在上一篇<elasticsearch7.X x-pack破解>中,我们启用了x-pack模块,elasticsearch集群中,如果使用了x-pack,那么集群中的各节点之间通讯 ...
- javascript代码重构需要考虑的问题(一)
1.将数组的长度进行存储,循环时就不用每次去读取一次数组长度,从而提升性能 例如: for (var i = 0, len = arr.length; i < len; i++) { //do ...
- Java集合(九)哈希冲突及解决哈希冲突的4种方式
Java集合(九)哈希冲突及解决哈希冲突的4种方式 一.哈希冲突 (一).产生的原因 哈希是通过对数据进行再压缩,提高效率的一种解决方法.但由于通过哈希函数产生的哈希值是有限的,而数据可能比较多,导致 ...
- 线程的同步机制:同步代码块&同步方法
解决存在的线程安全问题:打印车票时出现重票,错票 使用同步代码块的解决方案 TestWindow2 package com.aff.thread; /* 使用实现Runnable接口的方式,售票 存在 ...
- 画出决策边界线--plot_2d_separator.py源代码【来自python机器学习基础教程】
import numpy as np import matplotlib.pyplot as plt from .plot_helpers import cm2, cm3, discrete_scat ...