Skip to content

Java NIO

kcp edited this page Jul 13, 2020 · 2 revisions

title: Java-NIO.md date: 2019-04-20 12:16:01 tags: categories:

目录 start

  1. NIO
    1. Java IO 与 NIO的主要区别
    2. 通道(Channel)与缓冲区(Buffer)
      1. [1、缓冲区(Buffer)](#1、[缓冲区buffer]srcbuffertestbufferjava)
        1. 1、使用方法
        2. 2、核心属性
        3. 3、直接缓冲区与非直接缓冲区
      2. 2、通道
        1. 1、主要的Channel接口实现类
        2. 2、使用方法
          1. 获取通道
          2. 使用通道复制文件(本地IO)
        3. 3、通道之间的数据传输
        4. 4、分散(Scatter)与聚集(Gather)
        5. [5、字符集](#5、[字符集]srcchanneltestcharsetjava)
        6. 6、网路IO(核心内容)
          1. [1、TCP连接](#1、[tcp连接]srcchanneltcpchanneljava)
          2. [2、UDP连接](#2、[udp连接]srcchanneludpchanneljava)
      3. [3、管道](#3、[管道]srcpipetestpipejava)

目录 end|2020-04-27 23:42|


NIO

Java IO 与 NIO的主要区别

IO NIO
面向流 面向缓冲区
阻塞IO 非阻塞IO
选择器

通道(Channel)与缓冲区(Buffer)

Channel表示打开IO设备(如,文件、套接字)的连接。若要使用NIO,则需获取用于连接IO设备的管道以及容纳数据的缓冲区。然后操作缓冲区,对数据进行处理

在Java NIO中负责数据的存取,缓冲区就是数组用于操作不同类型的数据。不同的数据类型提供了不同的缓冲区,boolean除外。

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

1、使用方法

  1. 使用allocate()方法获取一定大小的缓冲区:ByteBuffer buffer = ByteBuffer.allocate(1024);

  2. 使用put()存入数据到缓冲区:buffer.put("hello".getBytes());

  3. 使用flip()切换成读取数据模式:buffer.flip();

  4. 使用get()获取缓冲区内的数据:byte[] bytes = new byte[buffer.limit()]; buffer.get(bytes);

2、核心属性

  • mark:标记, 表示记录当前position的位置,可通过reset()恢复到mark的位置
  • position:位置,缓冲区中正在操作数据的位置
  • limit:界限,缓冲区中可以操作数据的大小。 limit后的数据不可读写
  • capacity:容量,表示缓冲区中最大存储数据的容量,一旦声明不可改变

3、直接缓冲区与非直接缓冲区

  • 非直接缓冲区:通过allocate()方法分配缓冲区,缓冲区将建立在JVM的内存中

  • 直接缓冲区:通过allocateDirect()方法分配缓冲区,缓冲区将直接建立在物理内存中

2、通道

Channel表示IO源与目标打开的连接,类似于传统的流。不过Channel不能直接访问数据,Channel只能与Buffer进行交互

1、主要的Channel接口实现类

  • FileChannel
  • SocketChannel
  • ServerSocketChannel
  • DatagramChannel

2、使用方法

获取通道
  • Java针对支持通道的类提供getChannel()方法,这些类有:

    • 本地IO

      1. FileInputStream/FileOutputStream
      2. RandomAccessFile
    • 网络IO

      1. Socket
      2. ServerSocket
      3. DatagramSocket
  • JDK 1.7中的NIO2针对各个通道提供了静态方法open()

  • JDK 1.7中的NIO2中的Files工具类的newByteChannel()方法

使用通道复制文件(本地IO)
  • 利用Channel完成文件的复制(非直接缓冲区).
    @Test
    public void test1() throws IOException {
        FileInputStream fis = new FileInputStream("JavaNIO.iml");
        FileOutputStream fos = new FileOutputStream("JavaNIO2.iml");

        // 获取通道
        FileChannel inputChannel = fis.getChannel();
        FileChannel outputChannel = fos.getChannel();

        // 分配指定大小的缓冲区
        ByteBuffer buffer = ByteBuffer.allocate(1024);

        // 将通道内的数据读入缓冲区
        while (inputChannel.read(buffer) != -1) {
            // 将缓冲区内的数据写入通道
            buffer.flip();  //  切换为读数据
            outputChannel.write(buffer);
            buffer.clear(); // 清空缓冲区
        }

        // 关闭通道
        outputChannel.close();
        inputChannel.close();
        fos.close();
        fis.close();
    }
  • 使用直接缓冲区复制文件(内存映射文件).
    @Test
    public void test2() throws IOException {
        // 使用Open()获取通道
        FileChannel inputChannel = FileChannel.open(Paths.get("JavaNIO.iml"), StandardOpenOption.READ);
        FileChannel outputChannel = FileChannel.open(Paths.get("JavaNIO2.iml"),
                StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.CREATE_NEW);

        // 内存映射文件,和使用allocateDirect()获取内存一样,内存在物理内存中
        MappedByteBuffer inputMappedBuffer = inputChannel.map(FileChannel.MapMode.READ_ONLY, 0, inputChannel.size());
        MappedByteBuffer outputMappedBuffer = outputChannel.map(FileChannel.MapMode.READ_WRITE, 0, inputChannel.size());

        // 直接对缓冲区进行读写操作
        byte[] bytes = new byte[inputMappedBuffer.limit()];
        inputMappedBuffer.get(bytes);
        outputMappedBuffer.put(bytes);

        // 关闭通道
        inputChannel.close();
        outputChannel.close();
    }

3、通道之间的数据传输

  • 主要方法

    • transferFrom()
    • transferTo()
    @Test
    public void test3() throws IOException {
        // 使用Open()获取通道
        FileChannel inputChannel = FileChannel.open(Paths.get("JavaNIO.iml"), StandardOpenOption.READ);
        FileChannel outputChannel = FileChannel.open(Paths.get("JavaNIO2.iml"),
                StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.CREATE_NEW);

        // 通道间的数据传输(二用一)
        // inputChannel.transferTo(0, inputChannel.size(), outputChannel);
        outputChannel.transferFrom(inputChannel, 0, inputChannel.size());

        inputChannel.close();
        outputChannel.close();
    }

4、分散(Scatter)与聚集(Gather)

  • 分散读取(Scattering Reads): 将通道中的数据分散到多个缓冲区中

  • 聚集写入(Gathering Writes):将多个缓冲区内的数据聚集到通道中

指定字符集,避免乱码。常用于CharBuffer与ByteBuffer之间。

6、网路IO(核心内容)

传统IO为阻塞式的,NIO通过选择器实现非阻塞式IO

  • 核心内容

    • Channel
    • Buffer
    • Selector

主要Channel为SocketChannel与ServerSocketChannel;

主要Channel为DatagramChannel

3、管道

Java NIO 管道是2个线程之间的单向数据连接,Pipe有一个source通道和一个sink通道。数据会被写到sink通道,从source通道读取

Summary

Clone this wiki locally