为了账号安全,请及时绑定邮箱和手机立即绑定

Java I/O(3):NIO中的Buffer

标签:
Java

您好,我是湘王,这是我的慕课手记,欢迎您来,欢迎您再来~



之前在调用Channel的代码中,使用了一个名叫ByteBuffer类,它是Buffer的子类这个叫Buffer的类是专门用来解决高速设备与低速设备之间速度不匹配的问题的,也可以减少数据库的读写次数

它又分为输入缓冲区和输出缓冲区

很多初学者不明白缓冲缓存的区别我尝试着用大白话解释下

1、缓冲区需要定期进行刷新、清空、重置等操作,这些操作缓存可能并不需要比如做饭时砧板就是缓冲冰箱就是缓存因为从菜冰箱取出来到下锅需要不停地切每次都要清空了才能做下一道菜而冰箱是不用定期清空重置的除非停电东西都坏了);

https://img1.sycdn.imooc.com//635000e50001422f06490500.jpg



2、缓冲区核心作用是解耦设备间的速度制约,成为设备间的「缓冲」,而缓存则是用来加快读取的速度,减少重新计算或者重新从数据库获取的次数相比于每做一道菜都从菜场去买显然放在冰箱要快得多而相比于每次做菜都从冰箱拿当然从砧板上顺手拿要更快一些也就是:「菜场买菜速度磁盘 < 冰箱拿菜速度缓存 < 砧板拿菜速度缓冲区)」,就是这么个关系

3、缓冲区侧重于速度,侧重于写,而缓存侧重次数,侧重于读就像砧板侧重于切菜而冰箱侧重于存放

4、现在的缓存一般都很大甚至可以达到TB级别1TB=1024GB),缓冲是不可能这么大的当然你也可以把砧板搞成冰箱那么大反正我还没见过这种-_-!)。

 

以后再见到缓冲缓存的时候就可以拿家里的砧板和冰箱做对比

 

NIO中有八种类型的缓冲区:ByteBuffer、CharBuffer、DoubleBuffer、FloatBuffer、IntBuffer、LongBuffer, ShortBuffer和MappedByteBuffer,前七种分别对应基本数据类型,MappedByteBuffer专门用于内存映射。

https://img1.sycdn.imooc.com//635000f60001fc2f09070350.jpg


 

缓冲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()

读写过程大概就是这样的

https://img1.sycdn.imooc.com//635000fe0001654d06270141.jpg



还是昨天那句话如果你在大厂是自研类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说这么多已经足够了用代码去感受会更直接




感谢您的大驾光临!咨询技术、产品、运营和管理相关问题,请关注后留言。欢迎骚扰,不胜荣幸~




点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消