Java-NIO 开篇(1)
NIO简介
高性能的Java通信,离不开Java NIO组件,现在主流的技术框架或中间件服务器,都使用了Java NIO组件,譬如Tomcat、 Jetty、 Netty、Redis、RabbitMQ等的网络通信模块。在1.4版本之前, Java IO类库是阻塞式IO;从1.4版本开始,引进了新的异步IO库,被称为Java New IO类库,简称为Java NIO。 称“老的”阻塞式Java IO为OIO(Old IO)。总体上说, NIO弥补了原来面向流的OIO同步阻塞的不足,它为标准Java代码提供了高速的、面向缓冲区的IO。 学习NIO最主要的要理解Channel(通道)、Selector(选择器)、Buffer(缓冲区)三个核心组件。
在Java中, NIO和OIO的区别,主要体现在三个方面:
-
OIO是面向流(Stream Oriented)的, NIO是面向缓冲区(Buffer Oriented)的。
如何理解呢?非常简单,假设两个计算机的进程建立了通信,OIO的 read() 操作总是以输入流式的方式顺序地读取读取一个或多个字节,期间要么读要么不读,不能随意乱序读取。而写数据也只能往输出流写入数据。输入流和输出流是单向数据传输的。而NIO引入了Channel(通道)和Buffer(缓冲区)的概念。面向缓冲区的读取和写入,都是与Buffer进行交互。用户程序只需要从通道中读取数据到缓冲区中,或将数据从缓冲区中写入到通道中。 NIO不像OIO那样是顺序操作,可以随意地读取Buffer中任意位置的数据,可以随意修改Buffer中任意位置的数据。如下图所示,通道处于应用层,所有的通信都需要经过Channel通道:
-
OIO的操作是阻塞的,而NIO的操作是非阻塞的。
OIO阻塞: 当线程执行到read()和write()和连接服务器时的方法期间,如果没有对应的IO事件发生(客户端发送了连接请求、客户端发数据过来了)则一直在这些代码处等待,期间不做任何事情,线程挂起阻塞。直到发生了相应的IO事件才继续往下执行。并且如果还有其他的客户端触发了不是导致阻塞的IO事件也可能被阻塞。假设有两个客户端A和B,服务器伪代码如下所示,当没有客户端建立连接时则下面代码阻塞在第2行,假设此时A请求连接,那么下面代码就执行第2行然后阻塞在第3行。此时B也请求建立连接,这时候B是无法连接的,因为服务器代码阻塞在第3行。如果此时A发送了一个消息,那么下面程序继续执行,获取到A发生过来的消息msg。程序继续执行到第2行,如果因为B还在请求,直到这个时候B才能连接上服务器程序。这个例子就是OIO阻塞。
// OIO阻塞伪代码,先建立连接在读取客户端发来的数据的代码 1 while(true){ 2 sc = ssc.accept() // 接受客户端连接,阻塞方法 3 msg = sc.read() // 读取客户端发送过来的数据,阻塞方法 4 }NIO非阻塞: NIO的非阻塞实现方法是IO多路复用技术,通过selector来监测事件,如果没有事件发生则进行阻塞,如果有IO事件发生则获取到对应的事件处理对应的事件即可。首先A、B客户端没有连接时,下面程序执行到第2行进行阻塞,因为没有IO事件发生,如果此时A发起了连接则程序执行到第5行代码建立连接,如果此时B突然也请求连接,下面代码因为没有被阻塞经过10纳秒左右就能运行到第2行代码,然后发现B客户的请求连接事件,于是继续往下执行,同样执行到第5行代码。如果B请求连接的同时A突然发生了消息呢?那么没关系,events中有两个事件,通过for循环都能处理,A的消息发生请求会在第7行代码处理掉。下面的代码只是NIO的伪代码,实际上用法有一点点语法上的区别,逻辑是这样的。可以看见NIO是不会阻塞其他客户端的事件的,一般阻塞指的是较长时间的等待,例如1秒或者以上,下面也是单线程的服务器代码。如果你非要说10纳秒也是阻塞,那么下面的代码也确实无法0秒响应,实际上世界上也不存在这样的代码。什么是阻塞,我相信你已经有了自己的认知了!
// NIO非阻塞伪代码,先建立连接在读取客户端发来的数据的代码 1 while(true){ 2 events = selector.select() // 没有事件线程阻塞,有事件则接着往下运行 3 for(E e : event){ 4 if(e是请求事件){ 5 e.connect(); 6 }else if (e是读事件){ 7 msg = e.read(); 8 }.... 9 } 10 } -
OIO没有选择器( Selector)概念,而NIO有选择器的概念。
NIO技术的实现, 是基于底层的IO多路复用技术实现的,比如在Windows中需要select多路复用组件的支持,在Linux系统中需要select/poll/epoll多路复用组件的支持。 所以NIO的需要底层操作系统提供支持。而OIO不需要用到选择器selector,相信上面的NIO伪代码让你见识到了selector的威力!
到这里,你已经基本直到了NIO有多么牛了吧,但是还不够,前辈们的核心思想学习还刚刚开始,接下来需要学习非常核心的NIO类库的三大组件:Channel(通道)、Buffer(缓冲区)、Selector(选择器)。
Channel(通道)
Channel的角色和OIO中的Stream(流)是差不多的。 在OIO中,同一个网络连接会关联到两个流:一个输入流( Input Stream),另一个输出流(Output Stream), Java应用程序通过这两个流,不断地进行输入和输出的操作。在NIO中,一个网络连接使用一个Channel( 通道) 表示,所有的NIO的IO操作都是通过连接通道完成的。一个通道类似于OIO中的两个流的结合体,既可以从通道读取数据,也可以向通道写入数据。Channel和Stream的一个显著的不同是: Stream是单向的,譬如InputStream是单向的只读流, OutputStream是单向的只写流; 而Channel是双向的,既可以用来进行读操作,又可以用来进行写操作。如下图所示:

NIO中的Channel的主要实现有:
- FileChannel 用于文件IO操作
- DatagramChannel 用于UDP的IO操作
- SocketChannel 用于TCP的传输操作
- ServerSocketChannel 用于TCP连接监听操作
Selector(选择器)
首先回顾一下IO多路复用,指的是一个进程/线程可以同时监视多个socket连接,一旦其中的一个或者多个连接可读或者可写,该监听进程/线程能够进行IO事件的查询。在Java应用层面,Selector 选择器可以理解为一个IO事件的监听与查询器。通过选择器,一个线程可以查询多个通道的IO事件的就绪状态。什么是IO事件呢?表示通道某种IO操作已经就绪、或者说已经做好了准备。例如,如果一个新Channel连接建立成功了,就会在Server Socket Channel上发生一个IO事件,代表一个新连接一个准备好,这个IO事件叫做“接收就绪”事件。 再例如, 一个Channel通道如果有数据可读,就会发生一个IO事件,代表该连接数据已经准备好,这个IO事件叫做 ―“读就绪”事件。Java NIO将NIO事件进行了简化,只定义了四个事件,这四种事件用SelectionKey的四个常量来表示:
- SelectionKey.OP_CONNECT
- SelectionKey.OP_ACCEPT
- SelectionKey.OP_READ
- SelectionKey.OP_WRITE
Selector本质就是去查询这些IO就绪事件的,从编程实现维度来说, IO多路复用编程的第一步,是把通道Channel注册到选择器中,第二步则是通过选择器Selector所提供的事件查询(select)方法,这些注册的通道是否有已经就绪的IO事件(例如可读、可写、网络连接完成等)。由于一个选择器只需要一个线程进行监控,所以,我们可以很简单地使用一个线程,通过选择器去管理多个连接通道。与OIO相比, NIO使用选择器的最大优势:系统开销小,系统不必为每一个网络连接(文件描述符)创建进程/线程,从而大大减小了系统的开销。总之, 通过Java NIO可以达到一个线程负责多个连接通道的IO处理, 这是非常高效的。这种高效,恰恰就来自于Java的选择器组件Selector以及其底层的操作系统IO多路复用技术的支持。
缓冲区(Buffer)
应用程序通过缓冲区与通道建立数据读写交互,Buffer顾名思义:缓冲区,实际上是一个容器,一个连续数组。 Channel提供从文件、网络读取数据的渠道,但是读写的数据都必须经过Buffer。 如下图所示:

所谓通道的读取,就是将数据从通道读取到缓冲区中;所谓通道的写入,就是将数据从缓冲区中写入到通道中。缓冲区的使用,是面向流进行读写操作的OIO所没有的,也是NIO非阻塞的重要前提。Buffer类是一个抽象类,对应于Java的主要数据类型,在NIO中有8种缓冲区类,分别如下: ByteBuffer、 CharBuffer、 DoubleBuffer、 FloatBuffer、 IntBuffer、 LongBuffer、 ShortBuffer、MappedByteBuffer。 不同的Buffer子类,其能操作的数据类型能够通过名称进行判断,比如IntBuffer只能操作Integer类型的对象。 最常用的是ByteBuffer。如ByteBuffer子类就拥有一个byte[]类型的数组成员final byte[] hb,作为自己的读写缓冲区,数组的元素类型与Buffer子类的操作类型相互对应。那么在接下来的博文中将介绍Buffer具体的用法。
总结
NIO的知识体系就是围绕Buffer(缓冲区)、 Channel(通道)、Selector(选择器)三大核心组件的,下面学习只需要学习这三个核心组件的用法即可,最后将结合代码举例说明!
经典神书推荐:《Java高并发核心编程系列》——尼恩
相关文章:
Java-NIO 开篇(1)
NIO简介 高性能的Java通信,离不开Java NIO组件,现在主流的技术框架或中间件服务器,都使用了Java NIO组件,譬如Tomcat、 Jetty、 Netty、Redis、RabbitMQ等的网络通信模块。在1.4版本之前, Java IO类库是阻塞式IO&…...
VSCode 插件推荐
前言 关于开发用的插件就不做赘述了,网上面有很多文章都做了推荐,本文推荐几个好看的插件。 文件图标主题 Vscode icons Material Icon Theme 字体主题 推荐 One Dark Pro 其他 推荐一个生成好看代码的网址 https://carbon.now.sh/...
摄像部分时序
(1).,后摄像。 (2).,前摄像。 RCAM_TO_LEDDRV_STROBE_EN_CONN表面意思是:后置摄像头到led驱动闪光灯_使能。从时序图中看起来是连接到U17的,发现果然如此。 闪光灯温度检测,是检测闪光灯的温度。所以时序图…...
为什么 Golang Fasthttp 选择使用 slice 而非 map 存储请求数据
文章目录 Slice vs Map:基本概念内存分配和性能Fasthttp 中的 SliceMap性能优化的深层原因HTTP Headers 的特性CPU 预加载特性 结论 Fasthttp 是一个高性能的 Golang HTTP 框架,它在设计上做了许多优化以提高性能。其中一个显著的设计选择是使用 slice 而…...
C#设计模式教程(7):适配器模式
适配器模式的定义 适配器模式(Adapter Pattern)是一种结构型设计模式,它允许不兼容的接口之间能够相互合作。适配器的作用是解决那些因接口不兼容而不能一起工作的类的问题,它通过包装一个类的接口转换成另一个期望的接口。 适配器模式主要分为两种: 类适配器(Class Ad…...
1818:红与黑【解析】-------深度优先搜索
1818:红与黑 描述 有一间长方形的房子,地上铺了红色、黑色两种颜色的正方形瓷砖。你站在其中一块黑色的瓷砖上,只能向相邻的黑色瓷砖移动。请写一个程序,计算你总共能够到达多少块黑色的瓷砖。 输入 包括多个数据集合。每个数据集合的第一行…...
实验三 Oracle数据库的创建和管理
🕺作者: 主页 我的专栏C语言从0到1探秘C数据结构从0到1探秘Linux 😘欢迎关注:👍点赞🙌收藏✍️留言 🏇码字不易,你的👍点赞🙌收藏❤️关注对我真的很重要&…...
Mysql:重点且常用的 SQL 标签整理
目录 1 <resultMap> 标签 2 <sql> 标签 3 <where> 标签 4 <if> 标签 5 <trim> 标签 6 <foreach> 标签 7 <set> 标签 1 <resultMap> 标签 比如以下代码: <resultMap type"SysCollege" id&qu…...
云锁防火墙编译安装nginx-plugin模块
一般情况下,当用户安装云锁的时候,云锁会自动适配nginx版本,使用我们已经预编译好的包含云锁模块的nginx备份并替换掉您当前系统中使用的nginx。卸载时,会将系统原始nginx文件替换回来。因此,云锁可保护使用nginx搭建的…...
【服务器数据恢复】服务器迁移数据时lun数据丢失的数据恢复案例
服务器数据恢复环境&服务器故障: 一台安装Windows操作系统的服务器。工作人员在迁移该服务器中数据时突然无法读取数据,服务器管理界面出现报错。经过检查发现服务器中一个lun的数据丢失。 服务器数据恢复过程: 1、将故障服务器中所有磁盘…...
6.4.2转换文件
6.4.2转换文件 利用Swf2VideoConverter2可以很方便地将Flash动画(*.swf)转换为其它的视频格式。 1.单击“添加”按钮,在弹出的下拉菜单中选择“添加文件”,在弹出的“Open Swf Files(打开Swf文件)”窗口中选择swf文件(如:那些花…...
智能驾驶新浪潮:SSD与UFS存储技术如何破浪前行?-UFS篇
如果说SSD是赛道上的超级跑车,那UFS更像是专为智能汽车定制的高性能轻量化赛车。UFS采用串行接口技术,像是闪电侠一样,将数据传输的速度推向新高,大幅缩短了系统启动时间和应用程序加载时间,这对追求即时反应的ADAS系统…...
TS 学习笔录(持续更新中)
TS学习笔录 1、TS 数据类型有哪些?2、元组是什么?3、union(联合类型)& Literal(字面量类型)?4、any 和 unknown 的区别?5、Object 对象类型?6、type 、interface 、 class 之间…...
RabbitMQ安装和使用
简介 RabbitMQ是一套开源(MPL)的消息队列服务软件,是由LShift提供的一个Advanced Message Queuing Protocol (AMQP) 的开源实现,由以高性能、健壮以及可伸缩性出名的Erlang写成。所有主要的编程语言均有与代理接口通讯的客户端库…...
使用pyechart创建折线图
import json from pyecharts.charts import Line from pyecharts import options# 首先使用文件打开数据 f_us open(Desktop/python/Project/数据可视化/美国.txt,r,encoding"UTF-8") f_rb open(Desktop/python/Project/数据可视化/日本.txt,r,encoding"UTF-8…...
Vue3+Ts:使用i18n实现国际化与全局动态下拉框框切换语言
Vue3Ts:使用i18n实现国际化与全局动态下拉框框切换语言 一、下载依赖:二、创建ts文件并配置main.ts三,如何使用1.在<template>中使用2.在setup中使用 四、全局下拉框动态切换 一、下载依赖: npm install vue-i18nnex二、创…...
多目标优化中常用的差分进化算法DE【2】
# 多目标优化中常用的进化算法 1、链接一 2、链接二 #后续继续补充多目标的差分进化算法MODE的应用 此链接介绍很详细,此处用来分享学习,后续有问题会继续进行补充。 如果你觉得不错,佛系随缘打赏,感谢,你的支持是…...
游卡:OceanBase在游戏核心业务的规模化降本实践
从 2023 年 9 月测试 OceanBase,到如今 3 个核心业务应用 OceanBase,国内最早卡牌游戏研发者之一的游卡仅用了两个月。是什么原因让游卡放弃游戏行业通用的 MySQL方案,选择升级至 OceanBase?杭州游卡网络技术有限公司(…...
LightDB - oracle_fdw 过滤条件下推增强【24.1】
LightDB - oracle_fdw 过滤条件下推增强【24.1】 1. 字符串比较下推1.1 示例 2. 隐式转换下推2.1 示例 3. nvl 和trim 下推3.1 示例 LightDB 在24.1版本对oracle_fdw 的where下推进行了增强,新增对如下两种情况进行下推: 字符串比较下推,如 …...
【计算机网络】HTTP协议以及简单的HTTP服务器实现
文章目录 一、HTTP协议1.认识URL2.urlencode和urldecode3.HTTP协议格式4.HTTP的方法5.HTTP的状态码6.HTTP常见Header7.重定向8.长连接9.会话保持10.基本工具 二、简单的HTTP服务器实现1.err.hpp2.log.hpp3.procotol.hpp4.Sock.hpp5.Util.hpp6.httpServer.hpp7.httpServer.cc8.总…...
深入浅出Asp.Net Core MVC应用开发系列-AspNetCore中的日志记录
ASP.NET Core 是一个跨平台的开源框架,用于在 Windows、macOS 或 Linux 上生成基于云的新式 Web 应用。 ASP.NET Core 中的日志记录 .NET 通过 ILogger API 支持高性能结构化日志记录,以帮助监视应用程序行为和诊断问题。 可以通过配置不同的记录提供程…...
Lombok 的 @Data 注解失效,未生成 getter/setter 方法引发的HTTP 406 错误
HTTP 状态码 406 (Not Acceptable) 和 500 (Internal Server Error) 是两类完全不同的错误,它们的含义、原因和解决方法都有显著区别。以下是详细对比: 1. HTTP 406 (Not Acceptable) 含义: 客户端请求的内容类型与服务器支持的内容类型不匹…...
大数据零基础学习day1之环境准备和大数据初步理解
学习大数据会使用到多台Linux服务器。 一、环境准备 1、VMware 基于VMware构建Linux虚拟机 是大数据从业者或者IT从业者的必备技能之一也是成本低廉的方案 所以VMware虚拟机方案是必须要学习的。 (1)设置网关 打开VMware虚拟机,点击编辑…...
定时器任务——若依源码分析
分析util包下面的工具类schedule utils: ScheduleUtils 是若依中用于与 Quartz 框架交互的工具类,封装了定时任务的 创建、更新、暂停、删除等核心逻辑。 createScheduleJob createScheduleJob 用于将任务注册到 Quartz,先构建任务的 JobD…...
Qwen3-Embedding-0.6B深度解析:多语言语义检索的轻量级利器
第一章 引言:语义表示的新时代挑战与Qwen3的破局之路 1.1 文本嵌入的核心价值与技术演进 在人工智能领域,文本嵌入技术如同连接自然语言与机器理解的“神经突触”——它将人类语言转化为计算机可计算的语义向量,支撑着搜索引擎、推荐系统、…...
[Java恶补day16] 238.除自身以外数组的乘积
给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法,且在 O(n) 时间复杂度…...
华为云Flexus+DeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建
华为云FlexusDeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建 前言 如今大模型其性能出色,华为云 ModelArts Studio_MaaS大模型即服务平台华为云内置了大模型,能助力我们轻松驾驭 DeepSeek-V3/R1,本文中将分享如何…...
push [特殊字符] present
push 🆚 present 前言present和dismiss特点代码演示 push和pop特点代码演示 前言 在 iOS 开发中,push 和 present 是两种不同的视图控制器切换方式,它们有着显著的区别。 present和dismiss 特点 在当前控制器上方新建视图层级需要手动调用…...
【C++进阶篇】智能指针
C内存管理终极指南:智能指针从入门到源码剖析 一. 智能指针1.1 auto_ptr1.2 unique_ptr1.3 shared_ptr1.4 make_shared 二. 原理三. shared_ptr循环引用问题三. 线程安全问题四. 内存泄漏4.1 什么是内存泄漏4.2 危害4.3 避免内存泄漏 五. 最后 一. 智能指针 智能指…...
LabVIEW双光子成像系统技术
双光子成像技术的核心特性 双光子成像通过双低能量光子协同激发机制,展现出显著的技术优势: 深层组织穿透能力:适用于活体组织深度成像 高分辨率观测性能:满足微观结构的精细研究需求 低光毒性特点:减少对样本的损伤…...
