10分钟看懂,Java NIO 底层原理( 二 )

  • 写在前面
  • 1.1. Java IO读写原理
    • 1.1.1. 内核缓冲与进程缓冲区
    • 1.1.2. java IO读写的底层流程
  • 1.2. 四种主要的IO模型
  • 1.3. 同步阻塞IO(Blocking IO)
  • 1.4. 同步非阻塞NIO(None Blocking IO)
  • 1.5. IO多路复用模型(I/O multiplexing)
  • 1.6. 异步IO模型(asynchronous IO)
写在前面 大家好 , 我是 高并发的实战社群【疯狂创客圈】尼恩 。 Java NIO 、 Reactor模式等基础原理性的知识 , 非常重要 , 无论开发、还是面试 。 ? 很多的小伙伴 , 被java IO 模型中 , 搞得有点儿晕 , 一会儿是4种模型 , 一会儿又变成了5种模型 。 ? 很多的小伙伴 , 也被nio这个名词搞晕了 , 一会儿java 的nio 不叫 非阻塞io , 一会儿java nio 又是非阻塞io , 到底是啥呢?? 很多的小伙伴 , 被异步和非阻塞搞晕了 。 都非阻塞了 , 难道不是异步的吗?? 这这 , 好难呀 。 ? 此文 , 从底层入手 , 给各位小伙伴 , 起底一下 , java的四大io模型 。 需要面试的 , 或者没有弄清楚的小伙伴 , 彻底的有福了 。
顺便说明下:本文的内容只是一个初稿、初稿 , 本文的知识 , 在《Netty Zookeeper Redis 高并发实战》一书时 , 进行大篇幅的完善和更新 , 并且进行的源码的升级 。博客和书不一样 , 书的内容更加系统化、全面化 , 更加层层升入、层次分明、更多次的错误排查 , 请大家以书的内容为准 。 本文的最终内容 ,具体请参考疯狂创客圈 倾力编著 , 机械工业出版社出版的 《Netty Zookeeper Redis 高并发实战》一书。 ?
10分钟看懂,Java NIO 底层原理文章插图
?
1.1. Java IO读写原理无论是Socket的读写还是文件的读写 , 在Java层面的应用开发或者是linux系统底层开发 , 都属于输入input和输出output的处理 , 简称为IO读写 。 在原理上和处理流程上 , 都是一致的 。 区别在于参数的不同 。
用户程序进行IO的读写 , 基本上会用到read">先强调一个基础知识:read系统调用 , 并不是把数据直接从物理设备 , 读数据到内存 。 write系统调用 , 也不是直接把数据 , 写入到物理设备 。
read系统调用 , 是把数据从内核缓冲区复制到进程缓冲区;而write系统调用 , 是把数据从进程缓冲区复制到内核缓冲区 。 这个两个系统调用 , 都不负责数据在内核缓冲区和磁盘之间的交换 。 底层的读写交换 , 是由操作系统kernel内核完成的 。
1.1.1. 内核缓冲与进程缓冲区缓冲区的目的 , 是为了减少频繁的系统IO调用 。 大家都知道 , 系统调用需要保存之前的进程数据和状态等信息 , 而结束调用之后回来还需要恢复之前的信息 , 为了减少这种损耗时间、也损耗性能的系统调用 , 于是出现了缓冲区 。
有了缓冲区 , 操作系统使用read函数把数据从内核缓冲区复制到进程缓冲区 , write把数据从进程缓冲区复制到内核缓冲区中 。 等待缓冲区达到一定数量的时候 , 再进行IO的调用 , 提升性能 。 至于什么时候读取和存储则由内核来决定 , 用户程序不需要关心 。
在linux系统中 , 系统内核也有个缓冲区叫做内核缓冲区 。 每个进程有自己独立的缓冲区 , 叫做进程缓冲区 。
所以 , 用户程序的IO读写程序 , 大多数情况下 , 并没有进行实际的IO操作 , 而是在读写自己的进程缓冲区 。
1.1.2. java IO读写的底层流程用户程序进行IO的读写 , 基本上会用到系统调用read">首先看看一个典型Java 服务端处理网络请求的典型过程:
(1)客户端请求
Linux通过网卡 , 读取客户断的请求数据 , 将数据读取到内核缓冲区 。
(2)获取请求数据
服务器从内核缓冲区读取数据到Java进程缓冲区 。
(1)服务器端业务处理
Java服务端在自己的用户空间中 , 处理客户端的请求 。
(2)服务器端返回数据
Java服务端已构建好的响应 , 从用户缓冲区写入系统缓冲区 。
(3)发送给客户端
Linux内核通过网络 I/O, 将内核缓冲区中的数据 , 写入网卡 , 网卡通过底层的通讯协议 , 会将数据发送给目标客户端 。
1.2. 四种主要的IO模型服务器端编程经常需要构造高性能的IO模型 , 常见的IO模型有四种:
(1)同步阻塞IO(Blocking IO)
首先 , 解释一下这里的阻塞与非阻塞:
阻塞IO , 指的是需要内核IO操作彻底完成后 , 才返回到用户空间 , 执行用户的操作 。 阻塞指的是用户空间程序的执行状态 , 用户空间程序需等到IO操作彻底完成 。 传统的IO模型都是同步阻塞IO 。 在java中 , 默认创建的socket都是阻塞的 。