一.NIO与IO:

IO:  一般泛指进行input/output操作(读写操作),Java IO其核心是字符流(inputstream/outputstream)和字节流(reader/writer)做为基本进行操作,只能做单向操作,而IO的读写方式采用流的方式进行读写操作,如图所示

对于NIO既可以说是(NEW NIO) 也是(NON Blocking IO),为什么说他的性能和效率高于IO流,其的传输方式采用块传输方式,也就是使用缓冲区(buffer),使用channel(通道)进行双向传输。

其关键三个API为:

channel(通道): 实现数据的双向输出

Buffer(缓冲区):用于存储临时的读写数据

Selector(选择器) 若干客户端在Selector中注册自己,若干channel注册到Selector中,通过选择操作选出就绪的键,通道线程来实现少量线程的为多个客户端服务

二.Buffer缓冲区

什么是缓冲区?Buffer 是一个对象, 它包含一些要写入或者刚读出的数据。 在 NIO 中加入 Buffer 对象,体现了新库与原 I/O 的一个重要区别。在面向流的 I/O 中,您将数据直接写入或者将数据直接读到 Stream 对象中。

 在内存中开辟一段连续的区域,进行临时存储,实际上缓冲区为一个数组,在Buffer中可以进行存储的数据类型为:

  • CharBuffer

  • FloatBuffer

  • IntBuffer

  • DoubleBuffer

  • ShortBuffer

  • LongBuffer

  • ByteBuffer

这些基本的实现都继承自Buffer类。

Buffer中定义了4个属性为:

     private int mark = -1;   //此属性将在随后说到
private int position = 0;
private int limit;
private int capacity;

其中:

 
不变性(以1,2,3,4为从低到高排序)

position

即将被读或写的位置

2

limit

限制最大取出值/放入值,注意:

position < limit

3

capacity

缓冲区中可以存储的最大容量

4

mark

标记位置,用于标记某一次的读取/写入的位置

1

三.以ByteBuffer为例

在进行读写操作前需要开辟一个区域,对ByteBuffer进行初始化操作。

继承体系:

ByteBuffer bf = ByteBuffer.allocate(1024);

在使用allocate初始化中,进入方法中,当容量为负时,返回一个异常,反之初始化一个HeapByteBuffer

 public static ByteBuffer allocate(int capacity) {
if (capacity < 0)
throw new IllegalArgumentException();
return new HeapByteBuffer(capacity, capacity);
}

并进行对属性进行赋值并创建一个相应容量的Byte数组

HeapByteBuffer(int cap, int lim) {            // package-private

        super(-1, 0, lim, cap, new byte[cap], 0);
/*
hb = new byte[cap];
offset = 0;
*/
}
 ByteBuffer(int mark, int pos, int lim, int cap,   // package-private
byte[] hb, int offset)
{
super(mark, pos, lim, cap);
this.hb = hb;
this.offset = offset;
}

由此完成便开辟一个区域用来存储数据

使用position 方法,limit方法,capacity方法查询相关信息,根据上面例子创建一个1024容量的缓冲区

    //存储位置
System.out.println("position:---"+bf.position());
//存储限制大小
System.out.println("limit:---"+bf.limit());
//存储容量
System.out.println("capacity:---"+bf.capacity());

此时position所指向0的位置。limiit所指向1024的位置。

随后在进行写操作时使用put进行写入操作,

         //为缓存区中存入数据
System.out.println("-------------put写入-------------");
//定义一个数据
String str= "123456";
bf.put(str.getBytes()); //将结果存储到新的字节数组中。
System.out.println("position:---"+bf.position());
System.out.println("limit:---"+bf.limit());
System.out.println("capacity:---"+bf.capacity());

此时:

那么position是如何得知的那?

通过在HeapByteBuffer中position初始化为0+数据转化为byte数组后的长度加起来得到position的值。并记录

写入时,position < limit    此时limit没有变化,

 public ByteBuffer put(byte[] src, int offset, int length) {

         checkBounds(offset, length, src.length);
if (length > remaining())
throw new BufferOverflowException();
System.arraycopy(src, offset, hb, ix(position()), length);
position(position() + length);
return this;

这就完成了数据的写入,当想在缓冲区中读取数据时,在需要先切换至读取状态,使用flip方法,在Buffer中的flip方法中 ,定义了将position赋给limit 限制读取的最大长度。并使position置为0

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

进行切换状态

  //写入 后切换为读取状态
System.out.println("-------------flip切换-------------");
/*
* 将limit = position
* 令 position = 0
* */
bf.flip(); //切换读取状态
System.out.println("position:---"+bf.position());
System.out.println("limit:---"+bf.limit());
System.out.println("capacity:---"+bf.capacity());

此时:

切换完成后,便进行读的操作。

 //切换成功后进行读取
System.out.println("-------------get读取-------------");
/*
* 从零开始读 ,读6个长度停止
* */
bf.get(str.getBytes(), 0, 6);
System.out.println("position:---"+bf.position());
System.out.println("limit:---"+bf.limit());
System.out.println("capacity:---"+bf.capacity());

你也可以并不止于读写,还可以清空缓冲区。使用clear方法

/**
* Clears this buffer. The position is set to zero, the limit is set to
* the capacity, and the mark is discarded.
*
* <p> Invoke this method before using a sequence of channel-read or
* <i>put</i> operations to fill this buffer. For example:
*
* <blockquote><pre>
* buf.clear(); // Prepare buffer for reading
* in.read(buf); // Read data</pre></blockquote>
*
* <p> This method does not actually erase the data in the buffer, but it
* is named as if it did because it will most often be used in situations
* in which that might as well be the case. </p>
*
* @return This buffer
*/
public final Buffer clear() {
position = 0;
limit = capacity;
mark = -1;
return this;
}

根据官方注释解释,这里清除并非真正的清除数据,而是抹去了存在的痕迹。使它被遗忘掉。数据也是可读的。

 //清空缓存区
System.out.println("-------------clear清空-------------"); bf.clear();
System.out.println("position:---"+bf.position());
System.out.println("limit:---"+bf.limit());
System.out.println("capacity:---"+bf.capacity());
//查看实际是否存在数据,并未被清除,只是被遗忘了。
System.out.println((char)bf.get(0));
System.out.println((char)bf.get(1));

此时:

在Buffer中还提供了一些方法如下:

rewind()

对缓冲区存入的数据重复读

remaining()

统计属性间的元素数limit - position

hasRemaining()

统计当前位置中是否还有数据

   

部分引用内容载自:https://www.cnblogs.com/imstudy/p/11108085.html

NIO(一):Buffer缓冲区的更多相关文章

  1. Nio再学习之NIO的buffer缓冲区

    1. 缓冲区(Buffer): 介绍 我们知道在BIO(Block IO)中其是使用的流的形式进行读取,可以将数据直接写入或者将数据直接读取到Stream对象中,但是在NIO中所有的数据都是使用的换冲 ...

  2. Java NIO 之 Buffer(缓冲区)

    一 Buffer(缓冲区)介绍 Java NIO Buffers用于和NIO Channel交互. 我们从Channel中读取数据到buffers里,从Buffer把数据写入到Channels. Bu ...

  3. JAVA NIO简介-- Buffer、Channel、Charset 、直接缓冲区、分散和聚集、文件锁

    IO  是主存和外部设备 ( 硬盘.终端和网络等 ) 拷贝数据的过程. IO 是操作系统的底层功能实现,底层通过 I/O 指令进行完成. Java标准io回顾 在Java1.4之前的I/O系统中,提供 ...

  4. Java NIO Buffer缓冲区

    原文链接:http://tutorials.jenkov.com/java-nio/buffers.html Java NIO Buffers用于和NIO Channel交互.正如你已经知道的,我们从 ...

  5. Java NIO中的缓冲区Buffer(一)缓冲区基础

    什么是缓冲区(Buffer) 定义 简单地说就是一块存储区域,哈哈哈,可能太简单了,或者可以换种说法,从代码的角度来讲(可以查看JDK中Buffer.ByteBuffer.DoubleBuffer等的 ...

  6. Java NIO之Buffer(缓冲区)

    ​ Java NIO中的缓存区(Buffer)用于和通道(Channel)进行交互.数据是从通道读入缓冲区,从缓冲区写入到通道中的. ​ 缓冲区本质上是一块可以写入数据,然后可以从中读取数据的内存.这 ...

  7. NIO入门之缓冲区Buffer

    缓存区 Buffer 是数据容器 ByteBuffer 可以存储除了 boolean 以外的其他 7 种Java基本数据类型,如 getInt.putInt Buffer 是抽象类,它有除了 Bool ...

  8. Java NIO(2):缓冲区基础

    缓冲区(Buffer)对象是面向块的I/O的基础,也是NIO的核心对象之一.在NIO中每一次I/O操作都离不开Buffer,每一次的读和写都是针对Buffer操作的.Buffer在实现上本质是一个数组 ...

  9. NIO基础学习——缓冲区

    NIO是对I/O处理的进一步抽象,包含了I/O的基础概念.我是基于网上博友的博客和Ron Hitchens写的<JAVA NIO>来学习的. NIO的三大核心内容:缓冲区,通道,选择器. ...

随机推荐

  1. 数据可视化之 图表篇(五) PowerBI图表不够炫酷?来看看这个

    现在这个大数据时代,每时每刻.各行各业都在产生多种多样的海量数据,如何简单高效的来理解.挖掘这些数据,发现背后的见解就非常重要. 本文介绍这个图表就可以帮你快速发现海量数据背后的见解,微软研究院打造的 ...

  2. Redis 相关运维操作

    背景 Redis作为目前全球最流行的KV存储,除了使用之外,还需要做好日常的运维工作.关于运维相关的工作,本文从以下方面进行介绍说明(Redis5.0以上): 内存方面 客户端连接方面 工具方面 说明 ...

  3. 创建MongoDB副本集教程

    今天有时间搞了一下mongoDB的副本集,遇到好多坑,写下此文,方便日后查阅! 本教程是在windows环境下安装测试的(我是本机一台 + 两台虚拟机) 本机:10.53.8.159 虚拟机一:10. ...

  4. J.U.C体系进阶(五):juc-collections 集合框架

    Java - J.U.C体系进阶 作者:Kerwin 邮箱:806857264@qq.com 说到做到,就是我的忍道! juc-collections 集合框架 ConcurrentHashMap C ...

  5. [译]使用DOT语言和GraphvizOnline来可视化你的ASP.NETCore3.0终结点01

    这是系列文章中的第一篇:使用GraphvizOnline可视化ASP.NETCore3.0终结点.. 第1部分-使用DOT语言来可视化你的ASP.NETCore3.0终结点(本文) 第2部分-向ASP ...

  6. Maven如何利用父工程对版本进行统一管理

    项目开发中我们该怎么对项目依赖的版本进行统一管理呢 答:创建一个父级工程,让所有的业务模块都继承该父级工程,即所有的业务都为Module 在父级工程pom文件添加<dependencyManag ...

  7. 「从零单排canal 05」 server模块源码解析

    基于1.1.5-alpha版本,具体源码笔记可以参考我的github:https://github.com/saigu/JavaKnowledgeGraph/tree/master/code_read ...

  8. vue-cli 2.x和3.x配置移动端适配px自动转为rem

    移动端适配一直都是个大问题,现在也出现了各种各样的解决方案,比如 rem, vw 百分比等,但是比较成熟的切比较容易编写的还是 rem,他是相对于根元素的 font-size 进行等比例计算的. 但是 ...

  9. vue项目打包踩坑记

    基于webpack+vue-cli下的vue项目打包命令是 npm run build ,等待打包完成后在根目录生成dist文件夹,里面包含了所有项目相关的内容. 注意:需要完整版的vue-cli项目 ...

  10. async基本使用

    async函数在使用上很简单,我们来看一下下面的例子 async function add(a,b){ return a+b } add(1,2).then((res) =>{ consoel. ...