• 04【NIO核心组件之Buffer】


    四、NIO核心组件之Buffer

    NIO是从JDK1.4版本开始引入的一个新的IO API,NIO支持面向缓冲区的、基于通道的IO操作。NIO将以更加高效的方式进行文件的读写操作。

    • BIO是同步阻塞IO,同步:即在同一时间点只能同时处理一个客户端连接,阻塞:即当调用方法获取数据时,如果没有可用的数据将会阻塞当前线程;

    • NIO则是同步非阻塞IO,NIO中有三大组件,分别是:Channel(通道),Buffer(缓冲区),Selector(选择器);当有客户端连接时,服务器可以获取与该客户端的连接Channel(通道),所有的通道都会被注册到Selector(选择器)上,当Channel上有读写数据时将会被Selector侦测到,服务器只需要派发一个线程来处理Selector上的事件即可;当前Channel如果没有读写数据时,Selector并不会一直阻塞的等待Channel的数据返回,而是轮询式的侦测所有的Channel,这是NIO非阻塞的核心;

    另外,客户端的连接都变成了Channel,这些Channel都注册到了Selector中,服务器再也不需要为每一个连接来创建一个独立的线程为之服务了;这也是NIO能够应对高并发的核心之一;

    4.1 Buffer相关子类

    Buffer本质上就是一块存储数据的内存,我们可以在这一块内存中进行读写操作。这与我们之前的数组非常类似,与数组不同的是,这块内存被封装成Buffer对象,并根据不同的数据类型提供有不同的Buffer子类。Java对Buffer提供了更多的API,使得Buffer功能更加强大;

    • Java中常见的Buffer如下:
      • CharBuffer
      • DoubleBuffer
      • IntBuffer
      • LongBuffer
      • ByteBuffer
      • ShortBuffer
      • FloatBuffer

    Tips:以上Buffer都继承与Buffer抽象类,StringBuffer和以上的Buffer并不是同一类的,没有继承与NIO包下的Buffer接口;

    4.2 Buffer中的属性

    Buffer是一块内存区域,这个块内存是有固定大小的,我们可以对这块内存区域进行读写操作;每一个Buffe都具备如下的属性;

    • capacity(容量) :在创建Buffer时需要指定Buffer的大小(容量),一旦创建后此Buffer容量不可修改;

    • position(位置):每次对Buffer进行读或写操作时,position都会自动递增,position表示下一个要读取或写入的数据索引。position不可大于limit;

    • limit(限制):表示Buffer可操作的数据区域,limit之后的数据不能读写;limit与capacity的关系为:limit<=capacity;默认情况下:

      • Buffer在写入模式中,limit=capacity
      • Buffer在读取模式中,limit=position

    capacity/position/limit示意图:

    在这里插入图片描述

    当往Buffer中添加数据时,position往后移动,capacity和limit保持不变:

    在这里插入图片描述

    mark和reset

    • mark(标记)/reset(重置):标记是一个索引,通过 Buffer 中的 mark() 方法 指定 Buffer 中一个特定的 position,之后可以通过调用 reset() 方法恢复到这个position;

    在这里插入图片描述

    mark/position/limit/capacity之间的关系:mark <= position <= limit <= capacity

    4.3 Buffer相关方法

    4.3.1 Buffer的创建

    • Buffer的创建:
      • public static ByteBuffer wrap(byte[] array):通过一个字节数组构建Buffer,通过该方法创建的Buffer,不会造成position的位移,position依旧是0;
      • public static ByteBuffer allocateDirect(int capacity):分配一个capacity大小的Buffer,该Buffer是基于直接内存;
      • public static ByteBuffer allocate(int capacity):分配一个capacity大小的Buffer,该Buffer是基于非直接内存;

    • Buffer的常用方法:
      • int capacity():返回 Buffer 的 capacity 大小
      • int position():返回Buffer的当前位置position
      • Buffer position(int n):将设置Buffer的当前position为 n,并返回修改后的 Buffer 对象
      • int limit():返回 Buffer的limit位置
      • Buffer limit(int n):设置新的limit位置,并返回一个具有新 limit 的缓冲区对象
      • Buffer mark():设置一个mark标记,为当前的position值;
      • Buffer reset():将位置 position 转到以前设置的 mark 所在的位置
      • boolean hasRemaining():判断Buffer中是否还有元素;position是否超过了limit;
      • int remaining():返回 position 和 limit 之间的元素个数
      • Buffer rewind():将位置设为为 0, 取消设置的 mark
      • public byte[] array():返回该Buffer的字节数组;注意,此方法不是去读取,而是将Buffer中的数据返回。该方法不会造成position的位移
      • Buffer flip()该方法将limit设置为position,然后将positon复位到0;此操作就是为了读取buffer做准备;因此我们称该方法将buffer切换为读模式;
      • Buffer clear()将position复位到0,将limit设置为capacity;所以说此方法将buffer切换为写模式;
      • public ByteBuffer compact()

    • 案例1-使用allocate创建一个Buffer,使用put添加数据:
    package com.dfbz.buffer;
    
    import org.junit.Test;
    
    import java.nio.ByteBuffer;
    
    /**
     * @author lscl
     * @version 1.0
     * @intro:
     */
    public class Demo01_Buffer的创建 {
        @Test
        public void test1() {
            // 分配一个8字节大小的Buffer缓冲区
            ByteBuffer buffer = ByteBuffer.allocate(8);
    
            System.out.println("-----------allocate------------");
            System.out.println(buffer.position());              // 0
            System.out.println(buffer.limit());                 // 8
            System.out.println(buffer.capacity());              // 8
    
            // 往Buffer中添加数据
            System.out.println("----------put(abcd)-------------");
            buffer.put("abcd".getBytes());
            System.out.println(buffer.position());              // 4
            System.out.println(buffer.limit());                 // 8
            System.out.println(buffer.capacity());              // 8
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 案例2-使用wrap创建一个Buffer,使用get获取数据:
    @Test
    public void test2() {
        ByteBuffer buffer = ByteBuffer.wrap("hello".getBytes());
    
        System.out.println("-----------wrap------------");
        System.out.println(buffer.position());              // 0
        System.out.println(buffer.limit());                 // 5
        System.out.println(buffer.capacity());              // 5
    
        byte[] data = new byte[buffer.limit()];
        
        // 从Buffer中读取数据
        buffer.get(data);
        System.out.println("-----------get------------");
        System.out.println(buffer.position());              // 5
        System.out.println(buffer.limit());                 // 5
        System.out.println(buffer.capacity());              // 5
        
        System.out.println("-----------println------------");
        System.out.println(new String(data, 0, data.length));         // hello
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    Tips:使用wrap创建一个有数据的Buffer时,position的位置依旧是0;

    4.3.2 Buffer的常用方法

    • 案例1-测试其他方法:
    package com.dfbz.buffer;
    
    import org.junit.Test;
    
    import java.nio.ByteBuffer;
    
    /**
     * @author lscl
     * @version 1.0
     * @intro:
     */
    public class Demo02_Buffer常用方法 {
        @Test
        public void test1() {
            // 分配一个8字节大小的Buffer缓冲区
            ByteBuffer buffer = ByteBuffer.allocate(8);
    
            System.out.println("-----------allocate------------");
            System.out.println(buffer.position());              // 0
            System.out.println(buffer.limit());                 // 8
            System.out.println(buffer.capacity());              // 8
    
            // 往Buffer中添加数据
            System.out.println("----------put(abcd)-------------");
            buffer.put("abcd".getBytes());
            System.out.println(buffer.position());              // 4
            System.out.println(buffer.limit());                 // 8
            System.out.println(buffer.capacity());              // 8
    
            // 将position=4这个位置打一个标记
            buffer.mark();
    
            // 再往Buffer中添加数据
            System.out.println("----------put(ef)----mark-------------");
            buffer.put("ef".getBytes());
            System.out.println(buffer.position());              // 6
            System.out.println(buffer.limit());                 // 8
            System.out.println(buffer.capacity());              // 8
    
            // position与limit之间的元素个数
            System.out.println("【remaining】--->" + buffer.remaining());         // 2
    
            // 重新回到之前标记的位置(4)
            System.out.println("-----------reset------------");
            buffer.reset();
            System.out.println(buffer.position());              // 4
            System.out.println(buffer.limit());                 // 8
            System.out.println(buffer.capacity());              // 8
    
            // position与limit之间的元素个数
            System.out.println("【remaining】--->" + buffer.remaining());         // 4
    
            // 将position设置为0,并取消之前设置过的mark
            System.out.println("----------rewind-------------");
            buffer.rewind();
            System.out.println(buffer.position());              // 0
            System.out.println(buffer.limit());                 // 8
            System.out.println(buffer.capacity());              // 8
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 案例2-测试hasRemaining()方法:
    @Test
    public void test2() {
        // 分配一个5字节大小的Buffer缓冲区
        ByteBuffer buffer = ByteBuffer.allocate(5);
    
        // 0(position) < 8(limit) = (true)
        System.out.println("【hasRemaining】--->" + buffer.hasRemaining());
    
        // 往Buffer中添加数据
        buffer.put("abc".getBytes());
    
        System.out.println(buffer.position());              // 3
        System.out.println(buffer.limit());                 // 5
        System.out.println(buffer.capacity());              // 5
        System.out.println("-----------------------");
    
        // 3(position) < 5(limit) = (true)
        System.out.println("【hasRemaining】--->" + buffer.hasRemaining());
    
        // 添加数据
        buffer.put("def".getBytes());               // 出现异常 java.nio.BufferOverflowException
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    4.3.2 Buffer的核心方法

    1)flip方法

    Buffer是一块内存区域,我们既可以往里面添加数据(put),也可以获取Buffer里面的数据(get);但是不管是读或者

    • Buffer flip()该方法将limit设置为position,然后将positon复位到0;此操作就是为了读取buffer做准备;因此我们称该方法将buffer切换为读模式;

    案例1-Buffer读取数据问题:

    package com.dfbz.demo;
    
    import org.junit.Test;
    
    import java.nio.ByteBuffer;
    
    /**
     * @author lscl
     * @version 1.0
     * @intro:
     */
    public class Demo03_flip方法 {
    
        @Test
        public void test1() throws Exception {
    
            // 创建一个容量为8的Buffer
            ByteBuffer buffer = ByteBuffer.allocate(8);
    
            buffer.put("abc".getBytes());
            System.out.println("-----------put(abc)------------");
            System.out.println(buffer.position());              // 3
            System.out.println(buffer.limit());                 // 8
            System.out.println(buffer.capacity());              // 8
    
            // 准备一个字节数组
            byte[] data=new byte[buffer.limit()];
    
            // 将数据读取到字节数组
            buffer.get(data);               // 出现异常BufferUnderflowException
    
            System.out.println(new String(data,0,data.length));
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    运行代码,出现Buffer溢出现象:

    在这里插入图片描述

    为什么会这样呢?原因是:不管是往Buffer中写入数据,还是读取数据,都会造成position的位移;

    • 测试代码:
    @Test
    public void test2() throws Exception {
    
        // 创建一个容量为8的Buffer
        ByteBuffer buffer = ByteBuffer.allocate(8);
    
        buffer.put("abc".getBytes());
        System.out.println("-----------put(abc)------------");
        System.out.println(buffer.position());              // 3
        System.out.println(buffer.limit());                 // 8
        System.out.println(buffer.capacity());              // 8
    
    
        System.out.println("-----------get-------------");
        // 准备一个字节数组
        byte[] data = new byte[3];
        // 将数据读取到字节数组
        buffer.get(data);
    
        System.out.println(buffer.position());              // 6
        System.out.println(buffer.limit());                 // 8
        System.out.println(buffer.capacity());              // 8
        
        System.out.println("------------------------");
        System.out.println(new String(data,0,data.length));         // 空
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    画图分析:

    在这里插入图片描述

    测试代码:

    @Test
    public void test3() throws Exception {
    
        // 创建一个容量为8的Buffer
        ByteBuffer buffer = ByteBuffer.allocate(8);
    
        buffer.put("abc".getBytes());
        System.out.println("-----------put(abc)------------");
        System.out.println(buffer.position());              // 3
        System.out.println(buffer.limit());                 // 8
        System.out.println(buffer.capacity());              // 8
    
        // 将position的位置设置为0
        buffer.position(0);
    
        System.out.println("-----------get-------------");
        // 准备一个字节数组
        byte[] data = new byte[3];
        // 将数据读取到字节数组
        buffer.get(data);
    
        System.out.println(buffer.position());              // 6
        System.out.println(buffer.limit());                 // 8
        System.out.println(buffer.capacity());              // 8
    
        System.out.println("------------------------");
        System.out.println(new String(data, 0, data.length));       // abc
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    执行程序:

    在这里插入图片描述

    将position设置为了0之后,发现可以正常读取,Buffer中帮我们提供了flip方法,该方法可以将Buffer的position复位到0,同时将Buffer中的limit设置为上一次的position位置,这样可以为读数据做准备,也可以限制读取的范围,防止读取到无效的数据;

    • Buffer flip()该方法将limit设置为position,然后将positon复位到0;此操作就是为了读取buffer做准备;因此我们称该方法将buffer切换为读模式;

    示意图:

    在这里插入图片描述

    • flip方法测试:
    @Test
    public void test4() throws Exception {
    
        // 创建一个容量为8的Buffer
        ByteBuffer buffer = ByteBuffer.allocate(8);
    
        buffer.put("abc".getBytes());
        System.out.println("-----------put(abc)------------");
        System.out.println(buffer.position());              // 3
        System.out.println(buffer.limit());                 // 8
        System.out.println(buffer.capacity());              // 8
    
    
        // 将limit设置为position(3),将position复位到0
        buffer.flip();
        System.out.println("-----------flip------------");
        System.out.println(buffer.position());              // 0
        System.out.println(buffer.limit());                 // 3
        System.out.println(buffer.capacity());              // 8
    
    
        System.out.println("-----------get-------------");
        // 创建一个大小为3的字节数组
        byte[] data = new byte[buffer.limit()];
        // 将数据读取到字节数组
        buffer.get(data);
    
        System.out.println(buffer.position());              // 3
        System.out.println(buffer.limit());                 // 3
        System.out.println(buffer.capacity());              // 8
    
        System.out.println("------------------------");
        System.out.println(new String(data, 0, data.length));       // abc
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    2)clear方法

    • Buffer clear()将position复位到0,将limit设置为capacity;所以说此方法将buffer切换为写模式;

    测试代码:

    package com.dfbz.demo;
    
    import org.junit.Test;
    
    import java.nio.ByteBuffer;
    
    /**
     * @author lscl
     * @version 1.0
     * @intro:
     */
    public class Demo04_clear方法 {
    
        @Test
        public void test1() throws Exception {
    
            // 创建一个容量为8的Buffer
            ByteBuffer buffer = ByteBuffer.allocate(8);
    
            buffer.put("abc".getBytes());
            System.out.println("-----------put(abc)------------");
            System.out.println(buffer.position());              // 3
            System.out.println(buffer.limit());                 // 8
            System.out.println(buffer.capacity());              // 8
    
            // limit=3,position=0
            buffer.flip();
            System.out.println("-----------flip------------");
            System.out.println(buffer.position());              // 0
            System.out.println(buffer.limit());                 // 3
            System.out.println(buffer.capacity());              // 8
    
            byte[] data=new byte[buffer.limit()];
            buffer.get(data);
            System.out.println(new String(data,0,data.length));         // abc
            System.out.println("-----------get------------");
            System.out.println(buffer.position());              // 3
            System.out.println(buffer.limit());                 // 3
            System.out.println(buffer.capacity());              // 8
    
            // limit=capacity(8),position=0
            buffer.clear();
            System.out.println("-----------clear------------");
            System.out.println(buffer.position());              // 0
            System.out.println(buffer.limit());                 // 8
            System.out.println(buffer.capacity());              // 8
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48

    需要注意的是,clear方法只是将limit设置为capacity,并且将position设置为0,并不会将Buffer中的数据清空;

    • 测试代码:
    @Test
    public void test2(){
        // 创建Buffer
        ByteBuffer buffer = ByteBuffer.allocate(8);
    
        buffer.put("abc".getBytes());
        System.out.println("-----------put(abc)------------");
        System.out.println(buffer.position());              // 3
        System.out.println(buffer.limit());                 // 8
        System.out.println(buffer.capacity());              // 8
    
        // position=0,limit=8
        buffer.clear();
        System.out.println("-----------clear------------");
        System.out.println(buffer.position());              // 0
        System.out.println(buffer.limit());                 // 8
        System.out.println(buffer.capacity());              // 8
    
        System.out.println("--clear方法只是将position设置为了0,并不代表Buffer中的数据被清空--");
        System.out.println((char)buffer.get(0));            // a
        System.out.println((char)buffer.get(1));            // b
        System.out.println((char)buffer.get(2));            // c
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    示意图:

    在这里插入图片描述

    3)compact方法

    如果Buffer中仍有未读的数据,且后续还需要这些数据,但是此时想要先写些数据,那么可以使用compact()方法。

    • public ByteBuffer compact():compact方法做如下三件事情
      • 1)将未读的数据拷贝到Buffer的起始位置(后续还可以读取到这些数据)
      • 2)然后将position设到最后一个未读元素正后面(保证对Buffer写数据时不会覆盖之前未读的数据)
      • 3)limit设置位capacity(可写的数据范围是整个Buffer的大小)

    示意图:

    在这里插入图片描述

    测试代码:

    package com.dfbz.buffer;
    
    import org.junit.Test;
    
    import java.nio.ByteBuffer;
    
    /**
     * @author lscl
     * @version 1.0
     * @intro:
     */
    public class Demo05_compact方法 {
        @Test
        public void test() {
            // 创建Buffer
            ByteBuffer buffer = ByteBuffer.allocate(8);
    
            buffer.put("abcde".getBytes());
            System.out.println("-----------put(abcde)------------");
            System.out.println(buffer);              // [pos=5 lim=8 cap=8]
    
            // limit=position,position=0
            buffer.flip();
    
            System.out.println("--------get---------");
            byte[] data = new byte[2];
            buffer.get(data);       // 进行数据读取
    
            System.out.println(buffer);              // [pos=2 lim=5 cap=8]
    
            /*
                limit=capacity=8
                    1) 将未读的数据拷贝到起始处
                        abcde(原始数据), cde(未读的数据)
                    2) position设置到最后一个未读的元素的正后面(position=3,保证下次要写入的时候不会覆盖之前未读的数据)
                    3) limit=capacity=8
             */
            buffer.compact();
            System.out.println("--------compact---------");
            System.out.println(buffer);              // [pos=3 lim=8 cap=8]
            System.out.println(new String(buffer.array(), 0, buffer.array().length));           // cdede
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    4)array方法

    • public byte[] array():返回该Buffer的字节数组;注意,此方法不是去读取,而是将Buffer中的数据返回。该方法不会造成position的位移

    案例1-使用get读取Buffer中的数据:

    package com.dfbz.buffer;
    
    import org.junit.Test;
    
    import java.nio.ByteBuffer;
    
    /**
     * @author lscl
     * @version 1.0
     * @intro:
     */
    public class Demo06_array {
        @Test
        public void test1() {
            // 创建Buffer
            ByteBuffer buffer = ByteBuffer.wrap("hello".getBytes());
    
            System.out.println("----------wrap('hello')-------------");
            System.out.println(buffer.position());              // 0
            System.out.println(buffer.limit());                 // 5
            System.out.println(buffer.capacity());              // 5
    
            byte[] data = new byte[buffer.limit()];
            buffer.get(data);
    
            System.out.println(new String(data,0,data.length));
            System.out.println("----------get-------------");
            System.out.println(buffer.position());              // 5
            System.out.println(buffer.limit());                 // 5
            System.out.println(buffer.capacity());              // 5
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 案例2-使用array获取Buffer中的数据:
    @Test
    public void test2() {
        // 创建Buffer
        ByteBuffer buffer = ByteBuffer.wrap("hello".getBytes());
    
        System.out.println("----------wrap('hello')-------------");
        System.out.println(buffer.position());              // 0
        System.out.println(buffer.limit());                 // 5
        System.out.println(buffer.capacity());              // 5
    
        // 获取Buffer中的数据,不会造成position的位移
        byte[] data = buffer.array();
    
        System.out.println(new String(data,0,data.length));
        System.out.println("----------get-------------");
        System.out.println(buffer.position());              // 5
        System.out.println(buffer.limit());                 // 5
        System.out.println(buffer.capacity());              // 5
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    4.3.3 只读Buffer

    默认情况下通过方法分配的缓冲区都是非只读缓冲区,即可读,又可写;Java提供API将一块常规Buffer转换为只读Buffer。只读Buffer与原Buffer共享数据,只不过它是只读的。如果原缓冲区的内容发生了变化,只读缓冲区的内容也随之发生变化

    • public ByteBuffer asReadOnlyBuffer():将Buffer转换为只读Buffer

    测试代码:

    package com.dfbz.demo;
    
    import org.junit.Test;
    
    import java.nio.ByteBuffer;
    
    /**
     * @author lscl
     * @version 1.0
     * @intro:
     */
    public class Demo04_只读Buffer {
    
        @Test
        public void test1() {
    
            // 分配一个Buffer(默认是非只读Buffer)
            ByteBuffer buffer = ByteBuffer.allocate(8);
            System.out.println("只读Buffer: " + buffer.isReadOnly());           // false
            System.out.println("------------");
    
            // 往Buffer中添加数据
            for (int i = 0; i < 8; i++) {
                buffer.put((byte) i);
            }
    
            // 将Buffer转换为只读Buffer
            ByteBuffer readOnlyBuffer = buffer.asReadOnlyBuffer();
    
            System.out.println("只读Buffer: " + readOnlyBuffer.isReadOnly());     // true
            System.out.println(readOnlyBuffer.position());          // 8
            System.out.println(readOnlyBuffer.limit());             // 8
            System.out.println(readOnlyBuffer.capacity());          // 8
            System.out.println("---------------");
    
            for (int i = 0; i < 8; i++) {
                System.out.println(readOnlyBuffer.get(i));
            }
            System.out.println("-------------------");
    
    
            // 改变原缓冲区的内容
            for (int i = 0; i < 8; i++) {
                byte b = buffer.get(i);
                b *= 10;
                buffer.put(i, b);
            }
    
            // 只读Buffer中的内容也随之改变
            for (int i = 0; i < 8; i++) {
                System.out.println(readOnlyBuffer.get(i));
            }
            System.out.println("-------------------");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55

    4.3.4 直接内存与非直接内存

    • 直接内存(系统内存):概指系统内存,而非堆内存;
    • 非直接内存(堆内存): 也可以称之为堆内存,运行JVM都会预先OLAL;SAWWWW分配一定内存,我们把JVM管理的这些内存称为堆内存(非操作系统直接内存),JVM会对这些内存空间的分配和回收进行管理。

    直接内存操作流程:

    在这里插入图片描述

    非直接内存操作流程:

    在这里插入图片描述

    直接内存它直接作用于本地系统的IO操作。而非直接内存,也就是堆内存中的数据,如果要作IO操作,会先从本进程内存复制到直接内存,再利用本地IO处理。很显然,直接内存会具有更高的效率。

    Buffer中提供allocateDirect方法可以创建直接内存,但它比申请普通的堆内存需要耗费更高的性能。另外,由于创建的是直接内存,不在属于JVM内存,因此他不会占用JVM的应用程序内存;因此,当你有很大的数据要缓存,并且它的生命周期又很长,那么就比较适合使用直接内存。只是一般来说,如果不是能带来很明显的性能提升,还是推荐直接使用堆内存。

    • public static ByteBuffer allocateDirect(int capacity):创建一个指定容量大小的直接内存Buffer;
    • public boolean isDirect():判断当前Buffer是否是基于直接内存;

    测试代码:

    package com.dfbz.demo;
    
    import org.junit.Test;
    
    import java.nio.ByteBuffer;
    
    /**
     * @author lscl
     * @version 1.0
     * @intro:
     */
    public class Demo04_直接内存与非直接内存 {
    
        @Test
        public void test1() throws Exception {
            // 直接内存
            ByteBuffer buffer_01 = ByteBuffer.allocateDirect(8);
    
            // 非直接内存
            ByteBuffer buffer_02 = ByteBuffer.allocate(8);
    
            System.out.println(buffer_01.isDirect());           // true
            System.out.println(buffer_02.isDirect());           // false
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 测试创建直接内存与非直接内存花费时间:
    @Test
    public void test2() throws Exception {
        long startTime = System.currentTimeMillis();
    
        for (int i = 0; i < 100000; i++) {
            ByteBuffer buffer = ByteBuffer.allocateDirect(102400);                  // 5167
    //            ByteBuffer buffer = ByteBuffer.allocate(102400);                      // 1268
        }
    
        long endTime = System.currentTimeMillis();
    
        System.out.println("花费时间: " + (endTime - startTime));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
  • 相关阅读:
    数据结构的一些算法
    h5开发网站-css实现页面的背景固定定位
    工业互联网行至深水区,落地的路要怎么走?
    购房,出资款性质如何认定?
    KMP算法(多种实现方式)
    wechaty消息防撤回功能
    Linux运维:网络管理
    Java学习笔记 --- 多线程
    JVM(一):jvm中的数据结构(内存模型):Java Virtual Machine Specification Runtime Data Areas
    软件测试100天上岸2-测试必须有策略
  • 原文地址:https://blog.csdn.net/Bb15070047748/article/details/125438301