【Java高级】Java NIO详解

本文为原创文章,转载请注明出处
查看[Java]系列内容请点击:https://www.jianshu.com/nb/45938443

Java NIO就是Java New IO的缩写,关于老的Java IO的进阶说明请移步:【Java高级】Java IO进阶

Java NIO主要依靠三个类:ChannelBufferSelector实现了一系列高效的异步阻塞IO模型。与同步IO相比,其具有高效、简单和异步的特点,使得程序可以在一个线程内管理多个输入、输出。

他们之间的关系可以描述为:

  • BufferChannel的底层存储结构,是按照字节、数字等存储的
  • Channel是一种通道,是对于文件的一种抽象,它使得我们可以访问诸如内存映射、文件加锁机制和文件间数据的快速传递等操作系统特性
  • Selector轮询Channel被注册的的事件,一旦Channel有事件通知,就处理发生在Channel上的事件

Buffer底层

Buffer是一个抽象类,实现了它的类包括ByteBuffer IntBuffer CharBuffer等,分别以byte int char等类型的数组作为存储单元。在NIO中,数据只能写到Buffer中,也读取的数据也只能存储在Buffer中。

Buffer的底层使用数组来进行存储,除了数组以外,使用三个指针来控制其读写内容:

  • capacity: 容量,就是数组的大小
  • position: 初始为0,下一次读写的位置指针(数组下标)
  • limit: 读写的极限位置,可被读写的数据区数组下标(比如数据大小为20,其中存储了10条数据,那么在读的时候limit=9)

此外,还有一个mark字段,与输入流类似,可以在当前位置打标签,然后通过reset将读取位置position设置到mark,即可实现重复数据读取。

在进行Buffer的使用之前需要先申请数组的大小,使用allocate方法来申请缓冲区数据大小。然后通过对这个数组的反复读写来实现数据的缓存功能

Buffer常用的一些方法如下:

  • public Buffer flip():读写模式转换,从读模式转换到写模式或者从写模式转换到读模式
  • public Buffer mark(): 在当前位置打标记
  • public Buffer reset(): 将position设置成mark位置
  • public Buffer clear(): 清空数据(实际是指针初始化为初始值)
  • public Buffer rewind(): position复位到0,可实现数据重复读取
  • public T compact(): 整理,将数据移动到从数据头开始

Channel

Channel是一种文件通道,通过Channel可以获取文件的各种属性等,它与上篇中介绍的流库很类似,但是Channel只能通过与Buffer交互来进行数据访问。同时,通道是双向的、可异步读写的。操作文件的是FileChannel

下面代码演示了FileChannelByteBuffer交互处理数据:

public class Test {

    public static void main(String[] args) throws Exception {
        writeData();
        readData();
    }

    // 写数据
    public static void writeData() throws Exception {
        FileChannel channel = FileChannel.open(Path.of("D:\\data.txt"), StandardOpenOption.WRITE); // 写模式
        ByteBuffer buffer = ByteBuffer.allocate(100);
        buffer.put("this is a test buffer.".getBytes());
        buffer.flip(); // 将buffer变为读模式,方便channel读
        channel.write(buffer); // 从buffer读数据
        channel.close();
    }

    // 读数据
    public static void readData() throws Exception {
        FileChannel channel = FileChannel.open(Path.of("D:\\data.txt"), StandardOpenOption.READ); // 读模式
        ByteBuffer buffer = ByteBuffer.allocate(100);

        int readSize = -1;
        do {
            readSize = channel.read(buffer); // 从channel读到buffer,这里buffer是写模式
            System.out.println("\nread size: " + readSize);
            buffer.flip(); // 转换为读模式

            // 取buffer中的数据
            while (buffer.hasRemaining()) System.out.print("/" + (char) buffer.get());

            buffer.clear();
        } while (readSize != -1);

        channel.close();
    }
}

输出结果:

read size: 22
/t/h/i/s/ /i/s/ /a/ /t/e/s/t/ /b/u/f/f/e/r/.
read size: -1

Selector

Selector通过【注册-轮询】的机制来使得程序允许以异步的方式获取输入输出。一个Selector中可以注册多个Channel,然后Selector就不断轮询这些Channel,一旦事件发生,在Selector里面就可以手动获取到。

首先,创建一个Selector

Selector selector = Selector.open();

其次,将Channel设置为非阻塞模式:

channel.configureBlocking(false);

注意的是,只有继承自SelectableChannel或者AbstractSelectableChannel的类才可以设置为非阻塞模式,才能使用Selector,我们上面使用的FileChannel不可以使用Selector,而SocketChannel SocketServerChannel等可以使用Selector

接着,可以为Selector注册感兴趣的事件:

SelectionKey key = channel.register(selector, SelectionKey.OP_WRITE);

第一个参数表示要注册的选择器,第二个参数表示感兴趣的事件,事件有多种。

SelectionKey中有一系列的方法来表示该通道是否就绪,是否可读、可写等,在实际使用过程中,也是使用SelectionKey来查询通道的状态。

AIO

AIO是一种除了注册事件以外,还注册了事件处理器的模型,可以初步理解为使用了观察者模式的NIO,这样就不用使用循环来不断轮询Selector状态了。

具体的实现可以自行查阅

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,542评论 6 504
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,822评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,912评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,449评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,500评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,370评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,193评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,074评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,505评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,722评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,841评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,569评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,168评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,783评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,918评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,962评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,781评论 2 354

推荐阅读更多精彩内容