美文网首页程序员Java学习笔记Java 杂谈
Java NIO(一):Channel 与 Buffer

Java NIO(一):Channel 与 Buffer

作者: 聪明的奇瑞 | 来源:发表于2018-03-01 14:30 被阅读108次

    Java NIO 概述

    • Java NIO 是 Java4 之后提供的一种带缓冲区、非阻塞 IO、且是双向通信的,数据是从通道到缓冲区,或从缓冲区到通道
    • 它由 Channel、Buffer、Selector 这几个部分构成了核心的 API

    Java IO 与 NIO 区别

    • Java IO:
      • 面向流、阻塞IO、单向通信
      • 从流中读取一个或多个字节、直至读取所有字节、它不能移动流中的数据
      • 优点:如果连接少,他的延迟是最低的,因为一个线程只处理一个连接,适用于少连接且延迟低的场景,比如说数据库连接
    • JavaNIO:
      • 面向缓冲、非阻塞IO、选择器、双向通信
      • 将数据读取到一个缓冲区,需要时可以在缓冲区前后移动,增加灵活性
      • 优点:阻塞业务处理但不阻塞数据接收,如果是需要管理同时成千上万个连接,这些连接每次只发送少量数据,如聊天室,适用于高并发且处理简单的场景
      • 缺点:NIO 可以使用一个或几个单线程管理多个通道(网络连接),但付出的代价是解析数据可能会比从一个阻塞流中读取数据更复杂

    Channel(通道)

    • Channel 类似流,但又有些不同:
      • 既可以从通道中读取数据,又可以写数据到通道。但流的读写通常是单向的
      • 通道可以异步地读写
      • 通道中的数据总是要先读到一个Buffer,或者总是要从一个Buffer中写入
    • Channel 是通道的高层接口,其有几个实现类:
      • FileChannel:从文件中读写数据,但无法设置为非阻塞模式
      • DatagramChannel:能通过 UDP 读写网络中的数据
      • SocketChannel:能通过 TCP 读写网络中的数据
      • ServerSocketChannel:可以监听新进来的TCP连接,像Web服务器那样。对每一个新进来的连接都会创建一个SocketChannel

    Buffer(缓冲区)

    • 缓冲区本质上是一块可以读写数据的内存,这块内存被包装成了 NIO 的 Buffer 对象,并提供一组方法,用来方便的访问该块内存。
    • 数据是从通道读入缓冲区,从缓冲区写入到通道中的
    • Buffer 是缓冲区的高层接口,其有几个实现类:
      • ByteBufferMappedByteBufferCharBufferDoubleBuffer
      • FloatBufferIntBufferLongBufferShortBuffer
    • 通过 allocate 方法分配一个指定大小的 Buffer
    ByteBuffer buf = ByteBuffer.allocate(48);
    CharBuffer buf = CharBuffer.allocate(1024);
    
    • 新创建的 Buffer 为写模式,向 Buffer 中写数据有两种方式:
      • 从 Channel 写到 Buffer
      int bytesRead = inChannel.read(buf);
      
      • 通过 put 方法写到 Buffer
      buf.put(127);
      
    • 在写模式下调用 flip() 时 Buffer 切换为读模式,从 Buffer 中读数据有两种方式:
      • 从Buffer读取数据到Channel
      int bytesWritten = inChannel.write(buf);
      
      • 通过 get 方法从 Buffer 中读取数据
      byte aByte = buf.get();
      
    • 常用方法:
      • flip():翻转缓冲区,切换输入/输出模式
      buf.put("header ".getBytes());    // 往缓冲区输入数据
      in.read(buf);      // 将数据从从通道读入缓冲区
      buf.flip();        // 翻转缓冲区
      out.write(buf);    // 输出缓冲区数据写入到通道
      
      • rewind():将 position 设回 0,所以可以重读 Buffer 中所有数据
      • clear():清空缓冲区,Buffer 切换回写模式,将 position 设回 0,但实际上 Buffer 中的数据并未被清除,只是标记回 0 后新写入的数据会覆盖原来的数据
      • compact():清除已读过的数据,未读的数据会被移至缓冲区起始处,Buffer 切换回写模式,新写入的数据将从缓冲区未读数据后面写入
      • mark():标记 Buffer 中一个特定的 position,之后可通过 reset() 方法恢复到这个 position
      • equals():比较两个 Buffer 是否相同
        • 它必须有相同的类型(int、byte 等)
        • Buffer 中剩余的 byte、char 等个数相同
        • Buffer 中所有剩余的 byte、char 等都相同
      • compareTo():比较两个 Buffer 大小,如满足以下条件则认为一个 Buffer 小于另一个 Buffer
        • 第一个不相等的元素小于另一个 Buffer 中对应的元素
        • 所有元素都相等,第一个 Buffer 剩余的空间小于第二个 Buffer

    Buffer 的 capacity,position 和 limit

    • Buffer 包含了三个属性:
      • capacity
        • 代表 Buffer 的容量大小
        • 一旦 Buffer 满了就需要将其清空(通过读取数据或清除数据)才能继续往缓冲区里写数据
      • position
        • 当写数据到Buffer中时 position 表示当前的位置,初始值为 0,最大值为 capacity - 1,每写入一个 byte、long 等数据时 position 会下移到下一个可写的 Buffer 单元
        • 当切换 Buffer 为读数据时,position 会被重置为 0,每读入一个 byte、long 等数据时下移到下一个可读的位置
      • limit
        • 在写模式下,limit 表示最多能写入多少数据,等于 capacity
        • 在读模式下,limit 表示最多能读到多少数据,等于于写模式下的 position 值

    示例

    • 使用 Buffer 读写数据一般遵循以下四个步骤:
      • 输入数据到 Buffer
      • 调用 flip() 翻转缓冲区为输出模式
      • 将 Buffer 中数据输出
      • 调用 clear() 方法或者 compact() 方法清空缓冲区

    可以通过 RandomAccessFile 对象的 getChannel 方法获取 Channel,因为数据无论如何变化终究还是以字节形式存储

    try (RandomAccessFile raf = new RandomAccessFile("/Users/linyuan/Documents/字目录.txt", "rw")) {
        // 获取通道 Channel
        FileChannel fileChannel = raf.getChannel();
        // 创建缓冲区 Buffer
        ByteBuffer byteBuffer = ByteBuffer.allocate(52);
        // 将通道的数据读入缓冲区
        int bytesRead = fileChannel.read(byteBuffer);
        while (bytesRead != -1) {
            // 切换缓冲区模式
            byteBuffer.flip();
            // 读取缓冲区字节
            while (byteBuffer.hasRemaining()) {
                System.out.print((char) byteBuffer.get());
            }
            byteBuffer.clear();
            bytesRead = fileChannel.read(byteBuffer);
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
    

    相关文章

      网友评论

        本文标题:Java NIO(一):Channel 与 Buffer

        本文链接:https://www.haomeiwen.com/subject/fkbgxftx.html