前言
非阻塞 IO, 也被称之为新 IO, 它重新定义了一些概念.
缓冲 buffer
通道 channel
通道选择器
BIO 阻塞 IO, 几乎所有的 java 程序员都会的字节流, 字符流, 输入流, 输出流等分类就是针对 BIO 而言的. 我们在使用 BIO 的时候都是建立基本的节点流然后用过滤流进行包装.
不同于 BIO,NIO 所有的 IO 操作都是通过通道读写 buffer 完成的. 数据总是从通道读取到缓冲区中, 或者从缓冲区写入到通道中.
通道
NIO 的通道类似流, 但是有所不同.
既可以从通道中读取数据, 又可以写数据到通道. 而流的读写通常是单向的
通道就是类似与高速路, 数据通过 buffer 传递. 通道负责从 buffer 中读, 或者写入 buffer
通道可以异步读写
FileChannel 从文件中读写数据
DatagramChannel 能通过 UDP 读写网络中的数据
SocketChannel 能通过 TCP 读写网络中的数据
ServerSocketChannel 可以监听新进来的 TCP 连接, 像 web 服务器那样. 对每一个新进来的连接都会创建一个 SocketChannel
Buffer
**Buffer 用来缓存数据,**NIO 中所有的操作都是基于缓冲区继续操作的, 所有的读写操作都是通过缓存区来进行完成. 缓冲区本质上是一块可以写入数据, 然后可以从中读取数据的内存. 这块内存被包装成 NIO Buffer 对象, 并提供了一组方法, 用来方便的访问该块内存.
- ByteBuffer
- CharBuffer
- DoubleBuffer
- FloatBuffer
- IntBuffer
- LongBuffer
- ShortBuffer
缓冲区基本属性
容量 (capacity): 表示 Buffer 最大数据容量, 缓冲区容量不能为负, 并且创建后不能更改.
限制 (limit)
位置 (position)
position 和 limit 的含义取决于 Buffer 处在读模式还是写模式. 不管 Buffer 处在什么模式, capacity 都代表容量.
![nio buffer 原理](https://bingyanglu.github.io/images/corejava/nio buffer 原理. jpg)
Buffer 有一个固定的大小值, 叫 "capacity". 一旦 Buffer 满了, 需要将其清空 (通过读数据或者清除数据) 才能继续写数据往里写数据.
写模式
当你写数据到 Buffer 中时, position 表示当前的位置. 初始的 position 值为 0, 当一个 byte,long 等数据写到 Buffer 后, position 会向前移动到下一个可插入数据的 Buffer 单元. position 最大可为 capacity - 1. 在写模式下, Buffer 的 limit 表示你最多能往 Buffer 里写多少数据. 写模式下, limit 等于 Buffer 的 capacity.
读模式
当读取数据时, 也是从某个特定位置读. 当将 Buffer 从写模式切换到读模式, position 会被重置为 0. 当从 Buffer 的 position 处读取数据时, position 向前移动到下一个可读的位置. 当切换 Buffer 到读模式时, limit 表示你最多能读到多少数据. 因此, 当切换 Buffer 到读模式时, limit 会被设置成写模式下的 position 值. 换句话说, 你能读到之前写入的所有数据(limit 被设置成已写数据的数量, 这个值等于在写模式下 position).
使用
使用 Buffer 读写数据一般遵循以下四个步骤:
写入数据到 Buffer
调用 flip()方法
从 Buffer 中读取数据
调用 clear()方法或者 compact()方法
当向 buffer 写入数据时, buffer 会记录下写了多少数据. 一旦要读取数据, 需要通过 flip()方法将 Buffer 从写模式切换到读模式. 在读模式下, 可以读取之前写入到 buffer 的所有数据.
一旦读完了所有的数据, 就需要清空缓冲区, 让它可以再次被写入. 有两种方式能清空缓冲区: 调用 clear()或 compact()方法. clear()方法会清空整个缓冲区. compact()方法只会清除已经读过的数据. 任何未读的数据都被移到缓冲区的起始处, 新写入的数据将放到缓冲区未读数据的后面.
使用案例如下:
- import java.io.FileInputStream;
- import java.nio.ByteBuffer;
- import java.nio.channels.FileChannel;
- import java.nio.charset.Charset;
- public class TestNio {
- public static void main(String[] args) throws Exception {
- /*FileOutputStream outputStream = new FileOutputStream("E:/i.txt");
- FileChannel channel = outputStream.getChannel();
- ByteBuffer buffer = ByteBuffer.wrap("hahahah".getBytes());
- channel.write(buffer);
- channel.close();*/
- FileInputStream inputStream = new FileInputStream("E:/i.txt");
- FileChannel channel = inputStream.getChannel();
- ByteBuffer buffer = ByteBuffer.allocate(10);
- while (true){
- // 读数据写入 buffer 如果读了 6 个数据 那么 positon limit 均为 6
- int read = channel.read(buffer);
- if(read == -1) break;
- // 把 position 设置为 0 写操作变为读操作(相对与 buffer 而言)
- buffer.flip();
- // hasRemaining 当 position 和 limit 相等的时候为 false
- while (buffer.hasRemaining()){
- byte b = buffer.get();
- System.out.println((char)b);
- }
- // clear 实际上是 positon 和 limit 均置为 0 数据实际上是未清楚的
- // 读操作变为写操作(相对与 buffer)
- buffer.clear();
- }
- channel.close();
- ByteBuffer wrap = new ByteBuffer.wrap("哈哈哈".getBytes());
- // 编码处理
- Charset gbk = Charset.forName("GBK");
- ByteBuffer encode = gbk.encode("哈哈哈");
- }
- }
我不能保证每一个地方都是对的, 但是可以保证每一句话, 每一行代码都是经过推敲和斟酌的. 希望每一篇文章背后都是自己追求纯粹技术人生的态度.
永远相信美好的事情即将发生.
来源: https://juejin.im/post/5aefa5816fb9a07ac5600b14