NIO
NIO是Java继传统的阻塞IO后推出的新的IO模型,是一种同步非阻塞的模型,还有一种异步IO是叫做NIO2(AIO),这种IO是IO模型中异步IO的实现,但是在Linux上由于epoll支持的不太好,所以并未广泛使用

IO模型

IO模型一共有五种

  1. 阻塞式IO
  2. 非阻塞式IO
  3. 异步IO
  4. 信号量IO
  5. IO复用

Java的BIO属于阻塞式IO,NIO属于非阻塞式(需要不断轮询),AIO属于异步IO(基于事件异步回调)

NIO的三个组成部分

  1. Channel
    • FileChannel: 从文件中读写数据
    • DatagramChannel: 通过 UDP 读写网络中数据
    • SocketChannel: 通过 TCP 读写网络中数据
    • ServerSocketChannel:作为服务端监听SocketChannel
  2. Buffer
    • ByteBuffer
    • CharBuffer
    • ShortBuffer
    • IntBuffer
    • LongBuffer
    • FloatBuffer
    • DoubleBuffer
      一般常用ByteBuffer
  3. Selector

ByteBuffer的常用方法

分配空间

方法allocate()
    public static ByteBuffer allocate(int capacity) {
        if (capacity < 0)
            throw createCapacityException(capacity);
        return new HeapByteBuffer(capacity, capacity, null);
    }

在堆Heap中分配一个缓冲区,用于读写数据,读写效率较低,受到GC的影响
e.g

var buffer = ByteBuffer.allocate(16);
方法allocateDirect()
    public static ByteBuffer allocateDirect(int capacity) {
        return new DirectByteBuffer(capacity);
    }

在用户直接内存空间分配一个缓冲区,用于读写数据,读写效率高(零拷贝的基础),不会收到GC影响,分配的效率低,有内存泄露的风险
e.g

var directBuffer = ByteBuffer.allocateDirect(16);

写入数据

方法put()
public abstract ByteBuffer put(byte b);

这里的实现类为image-1650288964356

Channel的的read()
public abstract int read(ByteBuffer dst) throws IOException;

读取数据

get()
public abstract byte get();

get()方法会让position读指针向后走,如果想重复读取数据

  • 可以调用rewind方法将position重新置为0
  • 调用get(int i)方法获取索引i的内容,他不会移动读指针
Channel的write()
    public abstract int write(ByteBuffer src) throws IOException;

e.g

public class TestByteBufferRead {
  public static void main(String[] args) {
    //
    var buffer = ByteBuffer.allocate(10);
    buffer.put(new byte[] {'a', 'b', 'c', 'd'});
    buffer.flip();
    //    从头开始读
    /*   buffer.get(new byte[4]);
    debugAll(buffer);
    buffer.rewind();
    buffer.get(new byte[4]);
    debugAll(buffer);*/
    //    mark & reset
    //    mark 做一个标记,记录position位置,reset是将position重置到mark的位置
    System.out.println((char) buffer.get());
    System.out.println((char) buffer.get());
    buffer.mark();
    System.out.println((char) buffer.get());
    System.out.println((char) buffer.get());
    buffer.reset();
    System.out.println((char) buffer.get());
    System.out.println((char) buffer.get());
    //    get(i) 不会改变读索引的位置
    System.out.println((char) buffer.get(3));
    debugAll(buffer);
  }

Q.E.D.