Java IO -Channels

 

在 👉Java IO -Streams 文章中写到

Java IO基于两个主要的抽象:  (Streams)和 通道 (Channels)。流是数据传输的通道,而通道是流的源头或目的地。

补个坑,接着上篇说完

引言

在现代软件开发中,Java 的 New Input/Output(NIO)已成为处理高并发 IO 操作的标准。Java NIO 提供了一套非阻塞的 IO 操作方式,而 Channels 是这一机制的核心。本文将深入探讨 Java Channels 的概念、使用方式以及与 AIO 的结合应用。

Java NIO 概述

Java NIO 是从 Java 1.4 开始引入的,它在 java.nio 包中提供了一系列用于 IO 操作的新类。NIO 支持面向缓冲区(Buffer)的 IO 操作,可以提供更高的吞吐量和更少的资源消耗。

Channels

Channels 的概念

Channels 是 Java NIO 中的一个核心概念,代表 IO 操作的通道。与流(Stream)相比,Channels 提供非阻塞 IO 操作(👀️ 这里并非说Channels是非阻塞的),适用于需要高性能和高并发的网络应用。另外流(Stream)是单向的,而Channels是双向的。

主要 Channel 类型

Java NIO 提供了多种 Channels,主要包括:

  • FileChannel:用于文件 IO。
  • SocketChannel:用于 TCP 网络连接。
  • ServerSocketChannel:用于监听和接受 TCP 连接。
  • DatagramChannel:用于 UDP 网络连接。


缓冲区(Buffer)与 Channels

Buffer 是一块可以读写数据的内存区域,与 Channels 结合使用实现高效数据传输。数据总是从 Buffer 读入或写入 Channel。

Channel 示例

文件 Channel 操作

FileChannel 可以用于对文件进行高效读写操作。以下是一个使用 FileChannel 读取文件的示例:

  1. Path path = Paths.get("example.txt");
  2. try (FileChannel fileChannel = FileChannel.open(path, StandardOpenOption.READ)) {
  3.    ByteBuffer buffer = ByteBuffer.allocate(48);
  4.    int bytesRead = fileChannel.read(buffer);
  5.    // 处理读取的数据
  6. } catch (IOException e) {
  7.    e.printStackTrace();
  8. }

网络 Channel 操作

使用 SocketChannel 和 ServerSocketChannel 可以进行网络通信。以下是使用 SocketChannel 发送数据的示例:

  1. try (SocketChannel socketChannel = SocketChannel.open()) {
  2.    socketChannel.connect(new InetSocketAddress("example.com", 12345));
  3.    ByteBuffer buffer = ByteBuffer.allocate(1024);
  4.    buffer.put("Hello, World!".getBytes());
  5.    buffer.flip();
  6.    while (buffer.hasRemaining()) {
  7.        socketChannel.write(buffer);
  8.   }
  9. } catch (IOException e) {
  10.    e.printStackTrace();
  11. }

Channels与流(Stream)的区别(NIO与BIO流的区别)

Channels允许非阻塞IO,而传统IO流是阻塞的。

传统IO流(阻塞):

  1. BufferedInputStream bis = new BufferedInputStream(new FileInputStream("file.txt"));
  2. int data = bis.read(); // 阻塞读取,线程刮起

Channels(可阻塞/可非阻塞):

  1. FileChannel channel = FileChannel.open(Paths.get("file.txt"), StandardOpenOption.READ);
  2. //channel.configureBlocking(false); true or false
  3. ByteBuffer buffer = ByteBuffer.allocate(48);
  4. int bytesRead = channel.read(buffer); // 非阻塞读取,无数据立刻返回-1 线程不挂起

Channel 的小伙伴-选择器(Selector)

Selector 用于监控多个 Channel 的 IO 状态,是实现高并发网络编程的关键组件。

  1. Set<SelectionKey> selectedKeys = selector.selectedKeys();
  2.    for (SelectionKey key : selectedKeys) {
  3.        if (key.isReadable()) {
  4.            // 处理可读Channel
  5.       }
  6.   }

Selector 配合Channel

Selector用于监控多个Channel的IO状态实现高并发

  1. Selector selector = Selector.open();
  2. SocketChannel socketChannel = SocketChannel.open();
  3. socketChannel.configureBlocking(false);
  4. socketChannel.register(selector, SelectionKey.OP_READ);
  5. //selector 使用
  6. while (selector.select() > 0) {
  7.    Set<SelectionKey> selectedKeys = selector.selectedKeys();
  8.    for (SelectionKey key : selectedKeys) {
  9.        if (key.isReadable()) {
  10.            // 处理可读Channel
  11.       }
  12.   }
  13. }

Channels 的小Tips

分散(Scatter)与聚集(Gather)

  • Scatter Reads:从 Channel 读取数据到多个 Buffers。

  • Gather Writes:将多个 Buffers 的数据写入同一个 Channel。

  • 示例

    Scatter读取:

    1. ByteBuffer headerBuffer = ByteBuffer.allocate(128);
    2. ByteBuffer bodyBuffer = ByteBuffer.allocate(1024);
    3. channel.read(new ByteBuffer[] { headerBuffer, bodyBuffer });

    Gather写入:

    1. ByteBuffer[] buffers = new ByteBuffer[] { headerBuffer, bodyBuffer };
    2. channel.write(buffers);

Java AIO

Java AIO 介绍

Java AIO(Asynchronous I/O)是在 Java 7 中引入的,提供了异步非阻塞 IO 操作。与 NIO 不同,AIO 允许应用程序发起 IO 请求后立即返回,操作系统完成 IO 操作后通过回调函数通知应用程序。

  1. AsynchronousFileChannel fileChannel = AsynchronousFileChannel.open(Paths.get("file.txt"));
  2. ByteBuffer buffer = ByteBuffer.allocate(1024);
  3. fileChannel.read(buffer, 0, buffer, new CompletionHandler<Integer, ByteBuffer>() {
  4.    public void completed(Integer result, ByteBuffer attachment) {
  5.        // 处理异步读取结果
  6.   }
  7.    public void failed(Throwable exc, ByteBuffer attachment) {
  8.        // 处理异常
  9.   }
  10. });

AIO 与 NIO Channels 对比

AIO 和 NIO 在实现上的主要区别在于 AIO 是完全异步的,而 NIO 是非阻塞但同步的。AIO 更适用于大量并发连接的场景。

总结

  1. IO 中的阻塞和非阻塞指的是进行IO操作时,线程是否被挂起
  2. IO 中同步和异步指的是读/写操作时是否顺序操作
  3. IO的管道有单向的有双向的,管道可阻塞也可不阻塞
  4. IO的管道根据操作对象的不同可以分为不同的具体的类。(但站在Linux/Unix的角度看待这个问题,其实都是一样的,一切皆文件File.挖个坑-后续接着补😂)