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的主要实现有:

  1. FileChannel 用于文件IO操作
  2. DatagramChannel 用于UDP的IO操作
  3. SocketChannel 用于TCP的传输操作
  4. 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高并发核心编程系列》——尼恩

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/330155.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

0间隔24h采集线报+源码的资源网

一款网站程序零间隔24h采集线报源码的资源网,更新下载类目的采集 及 导入,这款网站程序:jizhiCMS 高仿新版某刀资源网模板进行自动采集。 安装方法: 将根目录文件上传服务器 将根目录文件的sql.sql导入mysql数据库 环境需要支…

springmvc上传与下载

文件上传 结构图 导入依赖 <dependency><groupId>jstl</groupId><artifactId>jstl</artifactId><version>1.2</version></dependency><dependency><groupId>org.springframework</groupId><artifactId…

Golang 搭建 WebSocket 应用(二) - 基本群聊 demo

上一篇文章中&#xff0c;我们已经了解了 gorilla/websocket 的一些基本概念和简单的用法。 接下来&#xff0c;我们通过一个再复杂一点的例子来了解它的实际用法。 功能 这个例子来自源码里面的 examples/chat&#xff0c;它包含了以下功能&#xff1a; 用户访问群聊页面的…

基于JavaSocket重写Dubbo网络传输层

前言 我们知道&#xff0c;位于 Serialize 层上面的是负责网络传输的 Transport 层&#xff0c;它负责调用编解码器 Codec2 把要传输的对象编码后传输、再对接收到的字节序列解码。 站在客户端的角度&#xff0c;一次 RPC 调用的流程大概是这样的&#xff1a; Invoker 发起 …

JMeter请求参数Parameters,带中文或特殊字符(+/=)时,例如登录密码或者token等,需要勾选编码

以前的登录接口密码参数不包含特殊字符&#xff0c;为了安全&#xff0c;产品今天修改了需求&#xff0c;密码必须由数字&#xff0c;字母和特殊字符构成&#xff0c;之前利用JMeter接口编写的脚本报错了&#xff0c;调整了一下&#xff0c;里面踩了一点坑&#xff0c;记录下来…

AM5-DB低压备自投装置在河北冠益荣信科技公司洞庭变电站工程中的应用

摘 要&#xff1a;随着电力需求的不断增加&#xff0c;电力系统供电可靠性要求越来越高&#xff0c;许多供电系统已具备两回或多回供电线路。备用电源自动投入装置可以有效提高供电的可靠性&#xff0c;该类装置能够在工作电源因故障断开后&#xff0c;自动且迅速地将备用电源投…

SpringMVC JSON数据处理见解6

6.JSON数据处理 6.1.添加json依赖 springmvc 默认使用jackson作为json类库,不需要修改applicationContext-servlet.xml任何配置&#xff0c;只需引入以下类库springmvc就可以处理json数据&#xff1a; <!--spring-json依赖--> <dependency><groupId>com.f…

react umi/max 封装页签组件

1. models/tabs // 全局共享数据示例 import { useState } from react;const useUser () > {const [items, setItems] useState<any[]>([]); // 页签的全局Item数据const [key, setKey] useState<string>(/home); // 页签的高亮Keyreturn {items,setItems…

Alinx ZYNQ 7020 LED调试--in RAM

设置拨码开关为JTAG方式 烧写LED bit stream a. 点击“Program device”烧录程序到FPGA中&#xff08;重新上电程序就丢失了&#xff09; b. /01_led/led.runs/impl_1/led.bit 程序烧录到Flash中 ZYNQ与以往的直接烧录Flash不同&#xff0c;首先必须PS&#xff0c;然后烧…

C语言总结十二:文件操作详细总结

在操作系统中&#xff0c;为了统一对各种硬件的操作&#xff0c;简化接口&#xff0c;不同的硬件设备也都被看成一个文件。对这些文件的操作&#xff0c;等同于对磁盘上普通文件的操作。我们不去探讨硬件设备是如何被映射成文件的&#xff0c;把任意 I/O 设备&#xff0c;转换成…

边缘计算AI智能分析网关V4客流统计算法的概述

客流量统计AI算法是一种基于人工智能技术的数据分析方法&#xff0c;通过机器学习、深度学习等算法&#xff0c;实现对客流量的实时监测和统计。该算法主要基于机器学习和计算机视觉技术&#xff0c;其基本流程包括图像采集、图像预处理、目标检测、目标跟踪和客流量统计等步骤…

HTML快速上手

前腰&#xff1a;本文只是概括重要的 html 标签&#xff0c;这些标签的使用频率较高&#xff0c;更多标签相关的资源您可以跳转 Mmdn 进行深入的学习。 1.HTML 基础 就其核心而言&#xff0c;HTML 是一种相当简单的、由不同 元素 组成的标记语言&#xff0c;它可以被应用于文本…

一款基于Frida的Android- SO动态库逆向命令行工具

前言 YJ是一款基于Frida框架的款Native层逆向分析的交互式工具&#xff0c;就像在GUN-LINUX上使用GDB工具一样&#xff0c;设计YJ的灵感来自GNU-GDB调试工具&#xff0c;它通过交互命令模式轻松地向展示你想要窥探的内存数据 Frida是一个底层hook工具及框架。提供了hook工具的…

防火墙如何处理nat(私网用户访问Internet场景)

目录 私网用户访问Internet场景源NAT的两种转换方式NAT No-PAT NAPT配置思路规划 NAPT配置命令配置接口IP地址并将接口加入相应安全区域配置安全策略配置NAT地址池配置源NAT策略配置缺省路由配置黑洞路由 私网用户访问Internet场景 多个用户共享少量公网地址访问Internet的时候…

CAN记录仪在矿卡中的应用

CAN数据记录仪在矿卡中主要用于记录和监控车辆的运行数据&#xff0c;以保障安全和提高运营效率。那么就需要记录整车数据来进行车辆诊断分析&#xff0c;查找问题解决问题。 CAN数据记录仪可以记录矿卡的各种运行参数&#xff0c;如发动机转速、车速、制动状态、转向状态、油…

首届PolarDB开发者大会在京举办,阿里云李飞飞:云数据库加速迈向智能化

1月17日&#xff0c;阿里云PolarDB开发者大会在京举办&#xff0c;中国首款自研云原生数据库PolarDB发布“三层分离”新版本&#xff0c;基于智能决策实现查询性能10倍提升、节省50%成本。此外&#xff0c;阿里云全新推出数据库场景体验馆、训练营等系列新举措&#xff0c;广大…

Git项目分支管理规范

一、分支管理 创建项目时&#xff0c;会针对不同环境创建两个常设分支(也可以算主分支&#xff0c;永久不会删除) master&#xff1a;生产环境的稳定分支&#xff0c;生产环境基于该分支构建。仅用来发布新版本&#xff0c;除了从release测试分支或 hotfix-*Bug修复分支进行m…

redis数据安全(四)复制

关系数据库通常会使用一个主服务器向多个从服务器发送更新&#xff0c;并使用从服务器来处理所有读请求&#xff0c;Redis也采用了同样的方法来实现自己的复制特性&#xff0c;并将其用做扩展性能的一种手段。 一、特点&#xff1a; 1、异步复制&#xff1a;Redis默认使用的是…

Mysql 数据库DML 数据操作语言—— 对数据库表中的数据进行增删改

DML&#xff1a;数据操作语言&#xff0c;用来对数据库表中的数据进行增删改 前提&#xff0c;数据库里面有一张表&#xff0c;具体如何创建&#xff0c;请看上篇文章 1、增添数据 1.1、给指定字段增添数据 insert into tt4 (name,age) values (张三,18); 1.2、给全部字段添…

使用Markdown编辑器

这里写自定义目录标题 欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题&#xff0c;有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants 创建一个自定义列表如何创建一个…