nio什么协议(深入理解NIO系列-)
nio什么协议(深入理解NIO系列-)
2024-07-06 02:57:53  作者:欲望都市  网址:https://m.xinb2b.cn/sport/qni182571.html
Buffer简介

在Java NIO中,主要有三大基本的组件:buffer、Channel和Selector,上一篇文章我们具体介绍了Selector,现在让我们深入理解下在实际编程中使用的最多的Buffer。

定义

首先先让我们来认识一下Buffer:

A container for data of a specific primitive type.

A buffer is a linear, finite sequence of elements of a specific primitive type

在Buffer类的描述中,Buffer被定义为用于特定基本类型数据的容器,且是特定基本类型的线性优先元素序列。

Buffer提供了一个字节缓冲区,它可以从channels中读取数据到Buffer,也可以将Buffer中的数据写入到channels,所以NIO被定义为面向缓冲区编程,而IO则是被定义为面向流的编程。

底层实现

通过阅读源码让我们慢慢揭开Buffer的神秘面纱

public abstract class Buffer { // Invariants: mark <= position <= limit <= capacity private int mark = -1; private int position = 0; private int limit; private int capacity; public final Buffer limit(int newLimit); public final Buffer mark(); public final Buffer reset(); public final Buffer clear(); public final Buffer flip(); public final Buffer rewind(); // 剩余可读元素,limit - position public final int remaining(); // 是否是只可读缓冲区 public abstract boolean isReadOnly(); // 是否是堆外内存 public abstract boolean isDirect();

Buffer类一共有四个变量,被称之为状态变量

capacity:容量,必须初始化的值(因为底层是数组)limit:上界,缓冲区的临界区,即最多可读到哪个位置position:下标,当前读取到的位置(例如当前读出第5个元素,则读完后,position为6)mark:标记,备忘位置

四个状态大小关系

0 <= mark <= position <= limit <= capacity

通过访问方法我们可以控制变量的指向,来达到读取到我们想要的数据的目的,下面让我们结合源码和图来学习下各方法对缓冲区的操作

ByteBuffer buffer = ByteBuffer.allocate(n); (1)buffer.limit(5); (2)// 执行get操作会将position加1buffer.get();buffer.get();buffer.mark(); (3)buffer.get();buffer.get();buffer.reset(); (4)buffer.flip(); (5)buffer.rewind(); (6)buffer.reset(); (7)

(1)初始化Buffer

image.png

(2)limit

public final Buffer limit(int newLimit) { if ((newLimit > capacity) || (newLimit < 0)) throw new IllegalArgumentException(); // 设置limit的新位置 limit = newLimit; // 如果当前读取到位置大于新设置的可读上限,则将position重置为limit,意思是缓存区无法再往下读取了 if (position > limit) position = limit; // 如果mark的位置大于新设置的可读上限,则需要重置mark为-1;代表mark的位置失效了 if (mark > limit) mark = -1; return this; }

所以如果我们执行limit(5)操作,则上图变化为

nio什么协议(深入理解NIO系列-)(1)

image.png

(3)mark

public final Buffer mark() { // 记录当前读到的位置 mark = position; return this; }

mark操作之前Buffer已经被读取到第二个位置(此时position=2),准备要读第三个元素之前,我们执行mark操作后,则图变化为

nio什么协议(深入理解NIO系列-)(2)

image.png

(4)reset

// mark位置,将postion置为mark // 恢复到上次备忘的位置 public final Buffer reset() { int m = mark; if (m < 0) throw new InvalidMarkException(); position = m; return this; }

reset操作之前,我们执行了两次get操作,所以此时的position=4,状态变化为下图

nio什么协议(深入理解NIO系列-)(3)

image.png

执行reset后我们的状态又变为(3)操作

nio什么协议(深入理解NIO系列-)(4)

image.png

(5)flip

如果我们想重新读取上一次读取的内容,则可以执行flip操作

// 将缓冲区的内容切换为重新读取状态 public final Buffer flip() { limit = position; position = 0; mark = -1; return this; }

状态变化为

nio什么协议(深入理解NIO系列-)(5)

image.png

(6)rewind

重读缓冲区操作

public final Buffer rewind() { position = 0; mark = -1; return this; }

此时的操作结果与执行flip操作后一致;

但如果我们执行rewind之前不执行flip操作,则在(4)操作后,我们直接执行rewind,则状态变化为

nio什么协议(深入理解NIO系列-)(6)

image.png

(7)clear

恢复缓冲区至初始状态

public final Buffer clear() { position = 0; limit = capacity; mark = -1; return this; }

nio什么协议(深入理解NIO系列-)(7)

image.png

通过上面的实例及图讲解相信大家一定对Buffer加深了了解,所以我们再次简单的定义一下Buffer;Buffer缓冲区其实就是一个线性数组,通过mark、position、limit来控制读取和写入数组,补充一下Buffer的特性:

Buffer底层是线性数组,是有限的基本类型元素的组合Buffer可以提供一个固定大小的容器来读取和写入数据每个Buffer都是可读的,但只有选中的buffer才可写默认情况下,Buffer不是线程安全的,所以在多线程环境下操作同一个Buffer,一定要使用锁机制保证同步Buffer的实现类

上文我们对Buffer已经有了一定的了解,下面让我们来看看它有哪些实现吧;首先我们看看Buffer类结构图

nio什么协议(深入理解NIO系列-)(8)

image.png

一般的,我们将Buffer分为两类

ByteBuffer:字节缓冲区,NIO编程中最常用的缓冲区。主要包含两种,HeapByteBuffer和MappedByteBuffer基本类型Buffer:除了boolean类型之外的其他基本数据类型的缓冲区

在实际编程中,我们最常用的就是ByteBuffer,因为在实际IO中也都是通过字节流在交互,所以下面我们将重点讲ByteBuffer,废话不多说,先上源码看看

public abstract class ByteBuffer extends Buffer implements Comparable<ByteBuffer>{ final byte[] hb; // 数组的起始位置 final int offset; // 创建基于堆外内存的ByteBuffer public static ByteBuffer allocateDirect(int capacity) { return new DirectByteBuffer(capacity); } // 创建基于堆内存的ByteBuffer public static ByteBuffer allocate(int capacity) { if (capacity < 0) throw new IllegalArgumentException(); return new HeapByteBuffer(capacity, capacity); } // 字节序 // BIG_ENDIAN:最低地址存放最高有效字节 // LITTLE_ENDIAN:最低地址存放最低有效字节 // Java字节序:JAVA虚拟机中多字节类型数据的存放顺序,JAVA字节序也是BIG-ENDIAN。 public final ByteOrder order() { return bigEndian ? ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN; } // 读取指定位置的元素 public abstract byte get(int index); // 往指定位置写入元素 public abstract ByteBuffer put(int index, byte b); // 基于当前状态new一个新的ByteBuffer public abstract ByteBuffer duplicate(); // 是否是直接操作内存的Buffer;若是,则此Buffer直接操作JVM堆外内存 ,使用Unsafe实现;否则操作JVM堆内存 public abstract boolean isDirect(); // 丢弃已经读取的数据,保留未读取的数据,并使缓存中处于待填充状态 public abstract ByteBuffer compact(); // 从当前buffer中生成一个该buffer尚未使用部分的新的缓冲区,例如当前buffer的position为3,limit为5,则新的缓冲区limit和capacity都为2,offset的3,数据区域两者共享; public abstract ByteBuffer slice();

从ByteBuffer的源码来看,相比较于Buffer,其新增了几个重要的方法,下面让我们继续以前文的方式来学习下这些方法的作用

ByteBuffer buffer = ByteBuffer.allocate(8); // 执行get操作会将position加1buffer.get();buffer.get();buffer.compact(); (1)buffer.slice(); (2)

执行(1)操作前,状态图如下:

nio什么协议(深入理解NIO系列-)(9)

image.png

(1)compact

protected int ix(int i) { return i offset; } public ByteBuffer compact() { // 把已经读取的内容使用后面的内容覆盖 System.arraycopy(hb, ix(position()), hb, ix(0), remaining()); // 修改状态 // 将position置为limit - position position(remaining()); // limit为capacity limit(capacity()); // mark置为-1 discardMark(); return this; }

状态转换如下,这里我们可以看到,position已经被置为6,但是其实我们是想position置为0,这样才符合我们的初衷;所以,从这点分析来看,compact方法最好是等缓冲区全部被读完后使用,达到复用ByteBuffer的目的,否则就会造成空间浪费

nio什么协议(深入理解NIO系列-)(10)

image.png

(2) slice

// 实际是共享当前ByteBuffer数组的空间 public ByteBuffer slice() { return new HeapByteBuffer(hb,-1,0,this.remaining(),this.remaining(),this.position() offset); }

最终状态如图,其中带1的部分是为新缓冲区的状态:

nio什么协议(深入理解NIO系列-)(11)

image.png

HeapByteBuffer和MappedByteBuffer

说了这么多Buffer和ByteBuffer的方法,我们再回过头来看看类结构图中ByteBuffer的两个子类,HeapByteBuffer和MappedByteBuffer,通过对比的方式了解下这两者的区别

HeapByteBufferMappedByteBuffer实现byte[]数组Unsafe实现内存分配Java堆内存堆外内存适用场景复用经常释放和新建

这里就不具体介绍这两个类实现的源码了,有兴趣了解Unsafe的同学可以看下我团技术团队推的一篇文章【基本功】Java魔法类:Unsafe应用解析

(1)什么是Java堆内存和堆外内存?

Java堆内存:Java内存结构主要有三大块:堆内存、方法区和栈;堆内存(如下图中的Heap区)是JVM中最大的一块由年轻代和老年代组成,是Java中GC的主要操作空间,是Java进程内的单位

nio什么协议(深入理解NIO系列-)(12)

image.png

堆外内存:不属于Java直接管理的空间,是不能被GC,所以只能手动申请和释放;属于操作系统直接管理的内存空间,是Java进程外的单位

(2)创建和释放的效率对比?

因为JVM堆中分配和释放内存肯定比系统分配和创建内存高效,所以创建和释放MappedByteBuffer的代价比HeapByteBuffer得要高

(3)IO效率对比?

在回答这个问题之前,首先让我们来看一张图

nio什么协议(深入理解NIO系列-)(13)

image.png

在unix和linux的体系架构中,一般会分为用户态和内核态

用户态:上层应用程序的活动空间,应用程序的执行必须依托于内核提供的资源。内核态:用于控制计算机的硬件资源,并提供上层应用程序运行的环境。系统调用:为了使上层应用能够访问到这些资源,内核为上层应用提供访问的接口。

而三者之间的关系为:

nio什么协议(深入理解NIO系列-)(14)

image.png

从上述的了解中,我们可以得出内核态的效率会大于用户态的效率

而在平时的read/write操作中,应用程序在与I/O设备进行交互时是需要经历一个“内核缓冲区”的。而MappedByteBuffer就好比是“内核缓冲区”上的缓存,而HeapByteBuffer则是应用程序Java中的内存空间,所以结论显而易见,把一个MappedByteBuffer写入一个Channel的速度要比把一个HeapByteBuffer写入一个Channel的速度要快。

这里顺便附上一张性能对比图,详见NIO Buffer performance

nio什么协议(深入理解NIO系列-)(15)

image.png

FYIJava NIO 之 BufferByteBuffer详解用户态与内核态

微信搜索公众号"一只懒懒的coder"可关注我获取最新动态哦!!

  • 买不到高铁票怎么办(买不到高铁票应该怎么办)
  • 2024-07-06买不到高铁票应该怎么办可先买自己需要乘坐高铁车次的短途车票进站上车后会有高铁乘务员查票,可向其咨询如何补票,通常补票地点在办公车厢,不过补的票有可能是站票或查看出发地与目的地之间是否有中转站点,进行转车乘坐高速铁路,简称高。
  • 买电视怎么选择需要注意什么(有这份指南就够了)
  • 2024-07-06有这份指南就够了关注我,不错过每一篇好文时至今日,电视仍然是我们家庭中最重要的一个家电,可以不看,但不可以不买618又快来了,这是一年中最重要的一次购买电视的机会,咱们不可错过那怎么选电视,该选哪些呢?很多朋友还是不。
  • 今年省考成绩普遍偏高(省考成绩糟糕越学越差)
  • 2024-07-06省考成绩糟糕越学越差省考笔试成绩发布后一部分伙伴陷入了痛苦、迷茫“真的投入了不少时间学习,但申论越来越差”“阅卷是个迷,疫情背景下阅卷人都是凭感觉”“不会再花时间在申论上了,就是撞大运”“可能自己真的不适合学申论”……凡。
  • 演员于明加简介(于明加凭门第斩获电视剧制片业十佳演员奖)
  • 2024-07-06于明加凭门第斩获电视剧制片业十佳演员奖10月23日,第十届全国电视制片业十佳颁奖礼在陕西西安举行,多部不同类型不同风格的电视剧佳作集结于此,众位导演制片人编剧明星齐聚颁奖典礼现场共同角逐各大奖项于明加受邀出席颁奖典礼,一袭蓝色拼接礼服亮相。
  • 剥离试验机厂家:剥离试验机有哪些功能特点
  • 2024-07-06剥离试验机厂家:剥离试验机有哪些功能特点作为轻型的一种试验仪器,剥离试验机拥有测试精准、操作简单、扩充性强等优点,能对胶带、背胶、胶板、胶布、不干胶、双面胶、胶纸、魔术贴、胶黏剂、塑料薄膜、线材、电子零件等试样进行拉力、压力、剥离、离型力、。
  • 初中生英语听力材料(初中交流基础英语听力资料)
  • 2024-07-06初中交流基础英语听力资料听力题目:听下面5段对话,每段对话后有一个小题,从题中所给的A、B、C三个选项中选出最佳选项,并标在试卷的相应位置听完每段对话后,你都有10秒钟的时间来回答有关小题和阅读下一小题每段对话仅读一遍1.W。
  • 大众宝来一年用车成本(新款大众宝来养车成本来了)
  • 2024-07-06新款大众宝来养车成本来了新款宝来就要上市了!目前公布的预售价为11.39-14.29万元,累计推出5款车型对于一台每月紧凑级轿车销量排行榜前十名的“常客”来说,宝来的受关注度还是不低的那么,从用车成本的角度出发,新款宝来用车。
  • 燃油车会停产吗(假如燃油车停售)
  • 2024-07-06假如燃油车停售随着我国经济发展,私家车的持有量变得越来越多,问题随之而来堵车停车难等问题影响了广大车主,而空气污染却危害了全世界人类的生存环境为了减少燃油车对环境带来的危害,各个国家都开始推出“禁燃令”所谓的禁燃。
  • 叶璇最新机场消息(肚子上有三层赘肉)
  • 2024-07-06肚子上有三层赘肉现如今加入直播带货的明星是越来越多,果不其然明星的尽头是带货啊!与其他明星装扮精致带货不同,叶璇的状态是会让人大吃一惊的地步素颜出镜,身穿紧身衣,肚子上的赘肉叠了三层,港媒辣评“车轮腰”虽然不该对别人。
  • 河南暴雨陆巡水中救援(河南暴雨中的互联网救援)
  • 2024-07-06河南暴雨中的互联网救援来源:央广网央广网北京8月2日消息(记者郭佳丽)7月20日,河南郑州市出现大暴雨,局部特大暴雨随后,暴雨中心北移,新乡、安阳、鹤壁等地的部分地区出现强降雨郑州市气象服务中心官方微博“郑州气象”在20日。
  • 十二生肖所有的国产动漫(虹猫蓝兔说尽武侠情义)
  • 2024-07-06虹猫蓝兔说尽武侠情义题材新颖,推陈出新,我当初就是靠唱十二生肖闯江湖的op,才记熟了十二生肖这个《生肖传奇之十二生肖闯江湖》,感觉是flash动画,包括后面小海那个打年兽的,还有欢乐街的,都有一个特点,动物拟人,三观极正。