Java NIO完整总结

一、概述

Java NIO从JDK1.4开始出现,它只有一个目标:提高IO效率。老的IO包其实也都用NIO重新实现过,也可以利用NIO的一些特性提高效率,即使不写NIO的代码,平时使用IO也都用到了NIO的技术,只是隐藏到了底层的实现中。

NIO的高效率来自于类似操作系统IO处理的结构方式,也是NIO的核心部分:

  • channel(通道)
  • buffer(缓冲区)
  • selector(选择器)

IO与NIO

IO的流(stream)是阻塞的,当读或写的时候,线程被阻塞,这样就什么也干不了了。通常需要使用多线程来处理IO操作,当线程多了之后,阻塞的时候系统会频繁的上下文切换,那这些切换所消耗的资源都是没意义的,浪费了系统的资源。而NIO可以实现非阻塞IO模式,解决上面所导致的问题,这也是在大并发下常使用NIO来处理程序的原因。比如Tomcat的NIO模式,能让web服务器的效率提升很多。

Channel和Buffer

平时数据读写都是以流(Stream)的形式,而在NIO中是以缓冲区(Buffer)的形式来装载数据。读取数据传输的过程中数据存在Buffer中,而Buffer是通过Channel来传输的。 可以用经典的挖矿例子来理解:假设我们的数据是矿石,矿石怎么运输呢?需要通过轨道(Channel)和矿车(Buffer)。现在我们有一堆矿石要从A地点运到B地点,那么首先要在A点往矿车里装好矿石,矿车通过轨道到达B点,在B点将矿石从矿车上卸下。NIO的数据传输就是这么一个流程。

Channel(通道)又分为四种:

  • FileChannel——文件通道
  • DatagramChannel——UDP数据报通道
  • SocketChannel——TCP套接字通道
  • ServerSocketChannel——监听TCP连接的通道

而Buffer有以下几种实现:

  • ByteBuffer
  • CharBuffer
  • DoubleBuffer
  • FloatBuffer
  • IntBuffer
  • LongBuffer
  • ShortBuffer
  • MappedByteBuffer

Selector

官方文档中解释为Selector是SelectableChannel的多路复用器(FileChannel不是SelectableChannel,它是阻塞的),简单点理解就是channel管理器

在使用的时候,可以将N个channel以及相应的事件(如读、写、连接等)注册到selector上。通过selector来控制判断应该用哪个channel,做哪个操作。 Selector能监控和检测N个Channel,知晓哪些通道准备好了读或写的操作,这样我们就能直接让Selector来管理Channel。使用Selector的好处是可以让一个线程管理多个通道,如大并发导致线程过多的问题就能得到很好的解决。

二、Buffer

Buffer是缓冲区类,在官方文档中是这么描述的:一个用于特定基本类型数据的容器。也可以理解为特定类型的缓冲区。Buffer用于channel(通道)之间数据交互,所以需要支持读和写的操作。

使用Buffer一般分为以下几步:

  1. 将数据写入Buffer缓存中
  2. 调用flip()方法,从写模式切换到读模式
  3. 从Buffer中读取数据
  4. 调用clear()或compact()方法清除缓存中数据

下面是一个使用ByteBuffer的例子:

ByteBuffer buffer = ByteBuffer.allocate(1024);
//从channel读取数据,写入到Buffer中(channel放到后面再说)
int bytesRead = channel.read(buffer);
while(bytesRead != -1) {
    buffer.flip();//从写模式切换到读模式
    while(buffer.hasRemaining()) {
        System.out.print((char)buffer.get());
    }
    buffer.clear();//清空缓存,回到可写模式
    bytesRead = channel.read(buffer);
}

缓冲区就是一块内存,可以写入数据,然后在读取数据。Java使用Buffer对象包装这块内存,来访问这块缓冲区。

Buffer是一个抽象类,实现的子类有ByteBuffer、CharBuffer、DoubleBuffer、FloatBuffer、IntBuffer、LongBuffer、LongBuffer、ShortBuffer以及MappedByteBuffer,即除了boolean类型外的所有原始类型的缓冲区实现。

4个属性:mark <= position <= limit <= capacity

缓冲区的公共特性实现都在Buffer中提供。缓冲区有下面4个基本属性,也是理解缓冲区数据操作的关键:

public abstract class Buffer {
    //各变量位置: mark <= position <= limit <= capacity 
    private int mark = -1;//标记位置 
    private int position = 0;//位置:下一个要读取或写入的元素索引 
    private int limit;//限制:第一个不应该读取或写入的元素索引 
    private int capacity;//容量:元素数量 
}

各个数据类型的实现类使用数组存储相应类型的数据,如ByteBuffer:

public abstract class ByteBuffer extends Buffer {
   
    final byte[] bb;//堆缓冲数据
}

缓冲区的数据都是以数组形式存储的,读写的时候根据上面说的几个属性来确定当前的读写位置索引。
这里写图片描述

  • mark:用于标记位置,在移动索引的时候能快速恢复到之前标记的位置上。
  • position:当前索引位置,最大值为capacity-1。
  • limit:写模式下表示最多能写入多少数据,等于capacity;读模式下表示最多能读到多少数据,即从写模式切换到读模式时的position值。
  • capacity:容量,即这块内存块的容量

常用的几个关键方法实现如下:

//在当前位置作标记
public final Buffer mark() {
    mark = position;
    return this;
}

//复位到之前标记位置,如果未标记则抛出异常
public final Buffer reset() {
    int m = mark;
    if (m < 0) throw new InvalidMarkException();
    position = m;
    return this;
}

//清空缓冲区,可以看出只是让各个标志属性重新初始化,缓冲区中的数据没动
//下次读写直接覆盖,即简单又高效
public final Buffer clear() {
    position = 0;
    limit = capacity;
    mark = -1;
    return this;
}

//最常用的方法,当buffer要从写模式切换到读模式时,就需要执行这个方法
//限制退到当前位置(读的时候只到限制位),位置及标记重置
public final Buffer flip() {
    limit = position;
    position = 0;
    mark = -1;
    return this;
}

//位置和标记重置,进行重新读或写
public final Buffer rewind() {
    position = 0;
    mark = -1;
    return this;
}

//是否已到限制位置
public final boolean hasRemaining() {
    return position < limit;
}

数据分配

分配一个1024个字符的缓冲区,这个缓存区的容量(capacity)就是1024。

CharBuffer buf = CharBuffer.allocate(1024);

向Buffer中写数据的两种方式:

  • 从channel写到Buffer:channel.read(buffer)
  • 通过put()方法写入数据:buffer.put(110)

从Buffer读取数据的两种方式:

  • 从Buffer读取数据到channel:channel.wri
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值