简介: NIO支持面向缓冲区的基于 通道的IO操作。NIO将以更加高效的方式进行文 件的读写操作。

  • 需要获取 用于连接 IO 设备的通道以及用于容纳数据的缓冲区。然后操作缓冲区,对数据进行处理

    Buffer

    简介: 保存相同数据类型(基本数据类型)的容器

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

static XxxBuffer **allocate**(int capacity) : 创建一个容量为capacity 的 XxxBuffer 对象

概念:

容量(capacity) :表示 Buffer 最大数据容量,缓冲区容量不能为负,并且创 建后不能更改。

限制(limit):第一个不应该读取或写入的数据的索引,即位于limit 后的数据 不可读写。缓冲区的限制不能为负,并且不能大于其容量。

位置(position):下一个要读取或写入的数据的索引。缓冲区的位置不能为 负,并且不能大于其限制

标记(mark)与重置(reset):标记是一个索引,通过Buffer 中的mark() 方法 指 定 Buffer 中一个特定的position,之后可以通过调用 reset() 方法恢复到这 个 position.
image.png

直接与非直接缓冲区

简介: 直接缓冲区的内容驻留在常规的垃圾回收堆之外,所以对应用程序的内存需求量较小。避免受系统的影响

  1. Buffer的allocateDirect()工厂方法来创建。此方法返回的缓冲区进行分配和取消 分配所需成本通常高于非直接缓冲区。
  2. 通过FileChannel的 map() 方法将文件区域直接映射到内存中来创建。该方法返回 MappedByteBuffer

Channel

简介:直接与IO流节点连接, 只不过Channel 本身不能直接访问数据,Channel 只能与 Buffer 进行交互

实现类

  1. FileChannel:用于读取、写入、映射和操作文件的通道。 常用方法如下:
  2. DatagramChannel:通过 UDP 读写网络中的数据通道。
  3. SocketChannel:通过 TCP 读写网络中的数据。
  4. ServerSocketChannel:可以监听新进来的 TCP 连接,对每一个新进来的连接都会创建一个 SocketChannel。

    获取通道

    一、 **getChannel()** 方法
    以下支持FileInputStream、FileOutputStream、RandomAccessFile、DatagramSocket、Socket、 ServerSocket
    二、Files 类的静态方法 **newByteChannel()** 获 取字节通道。
    三、通过通道的静态方法**open()**打开并返回指定通道

方法:

  1. 将数据从源通道传输到其他 Channel 中

** transferFrom(fromChannel,count、position)** **transferTo(position、count、toChannel)**
image.png

步骤:

一、打开指定路径的文件通道,默认情况下,通道打开时用于读入
FileChannel channel = FileChannel._open_(path, StandardOpenOption._APPEND_);

二、将文件的一个区域映射到缓冲区中
MappedByteBuffer** buffer **= channel.map(FileChannel.MapMode._READ_ONLY_, 0, channel.size());

通过调用FileChannel类的map方法从这个通道中获得一个ByteBuffer缓冲区

  1. FileChannel.MapMode.READ_ONLY:所产生的缓冲区是只读的,任何对该缓冲区写入的尝试都会导致ReadOnlyBufferException异常。
  2. FileChannel.MapMode.READ_WRITE:所产生的缓冲区是可写的,任何修改都会在某个时刻写回到文件中。注意,其他映射同一个文件的程序可能不能立即看到这些修改,多个程序同时进行文件映射的确切行为是依赖于操作系统的。
  3. FileChannel.MapMode.PRIVATE:所产生的缓冲区是可写的,但是任何修改对这个缓冲区来说都是私有的,不会传播到文件中。

三、读写数据:
缓冲区支持顺序和随机数据访问,它有一个可以通过get()put()操作来移动的位置

  1. get(byte[] bytes)
  2. gte(byte[] bytes,int offset, int length)
  3. gteInt()
  4. getLong()....
    1. while (buffer.hasRemaining()){
    2. byte b = buffer.get();
    3. ...
    4. }
    5. //通过get(int i) 达到随机访问
    6. for (int i = 0; i < buffer.limit(); i++) {
    7. byte b = buffer.get(i);
    8. }
    Java对二进制数据使用高位在前的排序机制
  • 如果需要以低位在前的排序方式处理包含二进制数字的文件
    • buffer.order(ByteOrder._LITTLE_ENDIAN_);
  • 查询缓冲区内当前的字节顺序
    • buffer.order();

四、结束:在恰当的时机,以及当通道关闭时,会将这些修改写回到文件中

Tips:

  1. 计算文件的32位的循环冗余校验和(CRC32),
  • 用来判断一个文件是否已损坏的校验和,因为文件损坏极有可能导致校验和改变。
  • java.util.zip包中包含一个CRC32类

image.png

  1. 方法
  • FileInputStream、FileOutputStream、RandomAccessFile都可以获取这个管道
    • FileChannel getChannel()

文件加锁机制

简介:考虑一下多个同时执行的程序需要修改同一个文件的情形
image.png
image.png
image.png
**fileLock.close();**

  • 释放锁

文件加锁机制是依赖于操作系统的,下面是需要注意的几点:

  1. 在某些系统中,文件加锁仅仅是建议性的,如果一个应用未能得到锁,它仍旧可以向被另一个应用并发锁定的文件执行写操作。
  2. 在某些系统中,不能在锁定一个文件的同时将其映射到内存中。
  3. 文件锁是由整个Java虚拟机持有的。如果有两个程序是由同一个虚拟机启动的(例如Applet和应用程序启动器),那么它们不可能每一个都获得一个在同一个文件上的锁。当调用lock和tryLock方法时,如果虚拟机已经在同一个文件上持有了另一个重叠的锁,那么这两个方法将抛出OverlappingFileLockException。
  4. 在一些系统中,关闭一个通道会释放由Java虚拟机持有的底层文件上的所有锁。因此,在同一个锁定文件上应避免使用多个通道。
    1. 在网络文件系统上锁定文件是高度依赖于系统的,因此应该尽量避免