先回顾下NIO中的"三剑客"模型:selector、channel、buffer
对于网络通讯而言,代码最常处理的就是3件事:管理连接、读取数据、写入数据。上图中,selector就是用来管理连接的(通常只需要一个selector线程处理就行,可避免上下文切换),selector上注册了一堆channel(通道),channel是双向的(in/out),读写数据时,channel必须通过buffer(缓冲) 。selector会被不同的Event事件触发来选择(或者叫激活)某个channel。
这篇主要回顾buffer的用法,先看类图:

Buffer是一个抽象类,下面派生出了几种基本类型的子类,比如网络通讯中最常用的ByteBuffer,每个子类中真正用于存放数据的,是一个叫hb的数组。Buffer本身是可读可写的,为了方便标识读/写的位置,里面有3个非常重要的标识变量:
position :表示下1个读或写的位置(即:hb数组的下标索引)
limit:hb数组中有效数据的最大位置
capacity:hb数组的容量(注:capacity与limit的区别,比如1个数组的容量是10,但是里面只存放了6个有效数据,那么在读取时,capacity为10,而limit则为6)

jdk源码上,已经明确说明: position <= limit <= capacity。
经常容易忽视的方法:flip()
由于buffer是可读可写的,以IntBuffer为例,“写入时”每向buffer中放入1个int数字,position就向后移1位,所有数据写完后,position就指向最后1个数字的末尾了。“读取时”需要调用flip()方法把position重新移到位置0,才能读到数据。
package com.cnblogs.yjmyzz;
import java.nio.IntBuffer;
public class BufferTest {
public static void main(String[] args) {
IntBuffer intBuffer = IntBuffer.allocate(6);
System.out.println("after init => ");
System.out.println("pos:" + intBuffer.position() + ",limit:" + intBuffer.limit() + ",cap:" + intBuffer.capacity() + "\n");
System.out.println("begin write => ");
for (int i = 0; i < 4; i++) {
intBuffer.put(i + 1);
System.out.println("pos:" + intBuffer.position() + ",limit:" + intBuffer.limit() + ",cap:" + intBuffer.capacity());
}
System.out.println("\n-------before flip----------");
System.out.println("pos:" + intBuffer.position() + ",limit:" + intBuffer.limit() + ",cap:" + intBuffer.capacity());
intBuffer.flip();
System.out.println("\n-------after flip----------");
System.out.println("pos:" + intBuffer.position() + ",limit:" + intBuffer.limit() + ",cap:" + intBuffer.capacity() + "\n");
System.out.println("begin read =>");
for (int i = 0; i < 4; i++) {
System.out.println(intBuffer.get());
System.out.println("pos:" + intBuffer.position() + ",limit:" + intBuffer.limit() + ",cap:" + intBuffer.capacity());
}
}
}
输出结果:
after init => pos:0,limit:6,cap:6 begin write => pos:1,limit:6,cap:6 pos:2,limit:6,cap:6 pos:3,limit:6,cap:6 pos:4,limit:6,cap:6 -------before flip---------- pos:4,limit:6,cap:6 -------after flip---------- pos:0,limit:4,cap:6 begin read => 1 pos:1,limit:4,cap:6 2 pos:2,limit:4,cap:6 3 pos:3,limit:4,cap:6 4 pos:4,limit:4,cap:6
图解如下,这是刚初始化后:

写入4个元素后:

flip之后:

所有数据读取完成后:
参考文档:
https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/nio/Buffer.html
原文:https://www.cnblogs.com/yjmyzz/p/nio-buffer.html