Java I/O(3):NIO中的Buffer
您好,我是湘王,这是我的博客园,欢迎您来,欢迎您再来~
之前在调用Channel的代码中,使用了一个名叫ByteBuffer类,它是Buffer的子类。这个叫Buffer的类是专门用来解决高速设备与低速设备之间速度不匹配的问题的,也可以减少数据库的读写次数。
它又分为输入缓冲区和输出缓冲区。
很多初学者不明白「缓冲」和「缓存」的区别,我尝试着用大白话解释下:
1、缓冲区需要定期进行刷新、清空、重置等操作,这些操作缓存可能并不需要。比如做饭时,砧板就是缓冲,冰箱就是缓存,因为从菜冰箱取出来到下锅,需要不停地切、拍、剁,每次都要清空了才能做下一道菜,而冰箱是不用定期清空、重置的(除非停电,东西都坏了);

2、缓冲区核心作用是解耦设备间的速度制约,成为设备间的「缓冲」,而缓存则是用来加快读取的速度,减少重新计算或者重新从数据库获取的次数。相比于每做一道菜,都从菜场去买,显然放在冰箱要快得多;而相比于每次做菜都从冰箱拿,当然从砧板上顺手拿要更快一些。也就是:「菜场买菜速度(磁盘) < 冰箱拿菜速度(缓存) < 砧板拿菜速度(缓冲区)」,就是这么个关系;
3、缓冲区侧重于速度,侧重于写,而缓存侧重次数,侧重于读。就像砧板侧重于切菜,而冰箱侧重于存放;
4、现在的缓存一般都很大,甚至可以达到TB级别(1TB=1024GB),缓冲是不可能这么大的(当然你也可以把砧板搞成冰箱那么大,反正我还没见过这种-_-!)。
以后再见到缓冲、缓存的时候,就可以拿家里的砧板和冰箱做对比。
在NIO中有八种类型的缓冲区:ByteBuffer、CharBuffer、DoubleBuffer、FloatBuffer、IntBuffer、LongBuffer, ShortBuffer和MappedByteBuffer,前七种分别对应基本数据类型,MappedByteBuffer专门用于内存映射。

缓冲Buffer区实际上也是一个容器,一个由连续数组/集合组成的容器。Channel提供从文件、网络读取数据的渠道,但是读写的数据都必须经过Buffer。
向Buffer中写入数据的过程是:
1、从Channel写入数据到Buffer:channel.read(buf)
2、调用Buffer的put()方法:buf.put(Object)
从Buffer中读取数据的过程是:
1、从Buffer读取数据到Channel:channel.write(buf)
2、调用Buffer的get()方法:buf.get()
读写过程大概就是这样的:

还是昨天那句话:如果你在大厂是自研类RPC系统或类MQ中间件的,那这个一定要精通;否则理解就好,不必死磕。Buffer看到这里其实已经足够了。至于说:Buffer的属性、使用Buffer的步骤、JVM怎么在内存创建缓冲区等等,这些应该都是面霸必修课,但开发中极少用到。
还是用代码来说。
Buffer的常用方法:
// 分配JVM间接缓冲区
ByteBuffer buffer = ByteBuffer.allocate(32);
System.out.println("buffer初始状态: " + buffer);
// 将position设回8
buffer.position(8);
System.out.println("buffer设置后状态: " + buffer); System.out.println("测试reset ======================>>>");
// clear()方法,position将被设回0,limit被设置成capacity的值
buffer.clear();
System.out.println("buffer clear后状态: " + buffer);
// 设置这个缓冲区的位置
buffer.position(5);
// 将此缓冲区的标记设置5
// 如果没有buffer.mark();这句话会报错
buffer.mark();
buffer.position(10);
System.out.println("reset前状态: " + buffer);
// 将此缓冲区的位置重置为先前标记的位置(buffer.position(5))
buffer.reset();
System.out.println("reset后状态: " + buffer); System.out.println("测试get ======================>>>");
buffer = ByteBuffer.allocate(32);
buffer.put((byte) 'x').put((byte) 'i').put((byte) 'a').put((byte) 'n').put((byte) 'g');
System.out.println("flip前状态: " + buffer);
// 转换为读模式
buffer.flip();
System.out.println("get前状态: " + buffer);
System.out.println((char) buffer.get());
System.out.println("get后状态: " + buffer); System.out.println("测试put ======================>>>");
ByteBuffer pb = ByteBuffer.allocate(32);
System.out.println("put前状态: " + pb +
", put前数据: " + new String(pb.array()));
System.out.println("put后状态: " + pb.put((byte) 'w') +
", put后数据: " + new String(pb.array()));
System.out.println(pb.put(3, (byte) '3'));
// put(3, (byte) '3')并不改变position的位置,但put((byte) '3')会
System.out.println("put(3, '3')后状态: " + pb + ", 数据: " + new String(pb.array()));
// 这里的buffer是 xiang[pos=1 lim=5 cap=32]
System.out.println("buffer叠加前状态: " + buffer +
", buffer叠加前数据: " + new String(buffer.array()));
// buffer.put(pb);会抛异常BufferOverflowException
pb.put(buffer);
// 叠加后数据是wiang,因为buffer的position=1
System.out.println("put(buffer)后bb状态: " + pb + ", buffer叠加后数据: " + new String(pb.array())); // 重新读取buffer中所有数据
System.out.println("测试rewind ======================>>>");
buffer.clear();
buffer.position(10);
System.out.println("buffer当前状态: " + buffer);
// 返回此缓冲区的限制
buffer.limit(15);
System.out.println("limit后状态: " + buffer);
// 把position设为0,mark设为-1,不改变limit的值
buffer.rewind();
System.out.println("rewind后状态: " + buffer); // 将所有未读的数据拷贝到Buffer起始处,然后将position设到最后一个未读元素正后面
System.out.println("测试compact ======================>>>");
buffer.clear();
buffer.put("abcd".getBytes());
System.out.println("compact前状态: " + buffer);
System.out.println(new String(buffer.array()));
// limit=position;position=0;mark=-1; 翻转,也就是让flip之后的position到limit这块区域变成之前的0到position这块
// 翻转就是将一个处于存数据状态的缓冲区变为一个处于准备取数据的状态,或者相反
buffer.flip();
System.out.println("flip后状态: " + buffer);
// get()方法:相对读,从position位置读取一个byte,并将position+1,为下次读写作准备
System.out.println((char) buffer.get());
System.out.println((char) buffer.get());
System.out.println((char) buffer.get());
System.out.println("三次调用get后: " + buffer);
System.out.println(new String(buffer.array()));
// 把从position到limit中的内容移到0到limit-position的区域内
// position和limit的取值也分别变成limit-position、capacity
// 如果先将positon设置到limit,再compact,那么相当于clear()
buffer.compact();
System.out.println("compact后状态: " + buffer);
System.out.println(new String(buffer.array()));
Java一般用BufferedInputStream、BufferedReader等带缓冲的I/O类来处理大文件,但如果文件超大的话,比如达到GB,甚至TB级别,更快的方式是采用NIO中引入的文件内存映射方案:MappedByteBuffer。
你只需要MappedByteBuffer读写性能极高,最主要的原因就是因为它实现了对异步操作的支持,就可以了!
可以用大文件来试一下:
// ByteBuffer读取大文件
public static void useFileChannel() {
try{
FileInputStream fis = new FileInputStream("你电脑上已经存在的文件路径,例如C:\\file1");
FileChannel channel = fis.getChannel();
long start = System.currentTimeMillis();
ByteBuffer buff = ByteBuffer.allocate((int) channel.size());
buff.clear();
channel.read(buff);
long end = System.currentTimeMillis();
System.out.println(end - start);
fis.close();
channel.close();
} catch (IOException e) {
e.printStackTrace();
}
} // MappedByteBuffer读取大文件
public static void useMappedByteBuffer() {
try{
FileInputStream fis = new FileInputStream("你电脑上已经存在的文件路径,例如C:\\file1");
FileChannel channel = fis.getChannel();
long start = System.currentTimeMillis();
MappedByteBuffer mbb = channel.map(FileChannel.MapMode.READ_ONLY, 0, channel.size());
long end = System.currentTimeMillis();
System.out.println(end - start);
fis.close();
channel.close();
} catch (IOException e) {
e.printStackTrace();
}
} public static void main(String[] args) {
useFileChannel();
useMappedByteBuffer();
}
最后把这两个方法放到main()里面试试看效果。
NIO中的Buffer说这么多已经足够了,用代码去感受会更直接。
感谢您的大驾光临!咨询技术、产品、运营和管理相关问题,请关注后留言。欢迎骚扰,不胜荣幸~
Java I/O(3):NIO中的Buffer的更多相关文章
- Java NIO中的Buffer 详解
Java NIO中的Buffer用于和NIO通道进行交互.如你所知,数据是从通道读入缓冲区,从缓冲区写入到通道中的.缓冲区本质上是一块可以写入数据,然后可以从中读取数据的内存.这块内存被包装成NIO ...
- java NIO中的buffer和channel
缓冲区(Buffer):一,在 Java NIO 中负责数据的存取.缓冲区就是数组.用于存储不同数据类型的数据 根据数据类型不同(boolean 除外),提供了相应类型的缓冲区:ByteBufferC ...
- Java NIO中的Buffer
简介 Buffer缓冲区,首先要弄明白的是,缓冲区是怎样一个概念.它其实是缓存的一种,我们常说的缓存,包括保存在硬盘上的浏览器缓存,保存在内存中的缓存(比如Redis.memcached).Buffe ...
- Java NIO中的Buffer类
Buffer 缓冲,用于批量读写数据 Buffer是一个抽象类,基本数据类型都有实现类:XxxBuffer,比如ByteBuffer.CharBuffer.IntBuffer.DoubleBu ...
- NIO中的Buffer
public abstract class Buffer { // Invariants: mark <= position <= limit <= capacity private ...
- Java网络编程和NIO详解9:基于NIO的网络编程框架Netty
Java网络编程和NIO详解9:基于NIO的网络编程框架Netty 转自https://sylvanassun.github.io/2017/11/30/2017-11-30-netty_introd ...
- Java的BIO,NIO,AIO
Java中的IO操作可谓常见.在Java的IO体系中,常有些名词容易让人困惑不解.为此,先通俗地介绍下这些名词. 1 什么是同步? 2 什么是异步? 3 什么是阻塞? 4 什么是非阻塞? 5 什么是同 ...
- NIO之缓冲区(Buffer)的数据存取
缓冲区(Buffer) 一个用于特定基本数据类行的容器.有java.nio包定义的,所有缓冲区都是抽象类Buffer的子类. Java NIO中的Buffer主要用于与NIO通道进行交互,数据是从通道 ...
- NIO(一):Buffer缓冲区
一.NIO与IO: IO: 一般泛指进行input/output操作(读写操作),Java IO其核心是字符流(inputstream/outputstream)和字节流(reader/writer ...
随机推荐
- PostgreSQL定时备份
# PostgreSQL定时备份 # 版本:PostgreSQL14.0 # 一.如果你在pg_hba.conf中设定了密码登录,那么你就需要设置一个默认密码共psql登录的时候跳过验证.如果是roo ...
- CMAKE编译时如何自动下载第三方库并解压、安装到指定目录
GreatSQL社区原创内容未经授权不得随意使用,转载请联系小编并注明来源. 导语 在日常开发过程中难免会使用到第三方库或者需要将部分库分离另外存储,如果将库与代码放在一起难免会造成工程庞大,此时就可 ...
- Apache DolphinScheduler 使用文档(4/8):软件部署
本文章经授权转载,原文链接: https://blog.csdn.net/MiaoSO/article/details/104770720 目录 4. 软件部署 4.1 为 dolphinschedu ...
- Luogu4111 [HEOI2015]小Z的房间 (矩阵树,辗转相除高斯消元)
除法不能用于同余系,要辗转相除.注意不能加入柱子到矩阵. #include <iostream> #include <cstdio> #include <cstring& ...
- 应用集成-在Hexo、Hugo博客框架中使用Gitalk基于Github上仓库项目的issue无后端服务评论系统实践
关注「WeiyiGeek」公众号 设为「特别关注」每天带你玩转网络安全运维.应用开发.物联网IOT学习! 希望各位看友[关注.点赞.评论.收藏.投币],助力每一个梦想. 本章目录 目录 0x00 Gi ...
- Spring 04: IOC控制反转 + DI依赖注入
Spring中的IOC 一种思想,两种实现方式 IOC (Inversion of Control):控制反转,是一种概念和思想,指由Spring容器完成对象创建和依赖注入 核心业务:(a)对象的创建 ...
- Redis 哨兵机制
概述 由一个或多个 Sentinel(哨兵)实例组成的 Sentinel 系统可以监视任意多个主服务器,以及这些主服务器属下的所有从服务器,并在被监视的主服务器进入下线状态时,自动将下线主服务器属下的 ...
- 【python】pandas 索引操作
选择.修改数据(单层索引) 推荐使用.at..iat..loc..iloc 操作 句法 结果 备注 选择列 df[col] Series 基于列名(列的标签),返回Series 用标签选择行 df.l ...
- 【三维地图】开发攻略 —— 详解“GeoJSON”技术和应用场景
GeoJSON ,一个用于存储地理信息的数据格式.GoeJSON对象可以表示几何.特征或特征集合,支持:点.线.面.多点.多线.多面和几何集合.在基于平面地图,三维地图中都需要用到的一种数据类型. 由 ...
- VS2017Enterprise版本安装ImageWatch 2017问题解决
按照Andrei给的方法并不一定能成功. 方法如下: 1. Download the extension (ImageWatch.vsix) and open it using WinRAR 2. F ...