API学习

了解Java NIO (Channel/Buffer/Selec

2024-11-25  本文已影响0人  红与树

Java NIO

在 Java 编程中,Java NIO(New Input/Output)是一种高性能的 I/O 操作方式,它提供了一套不同于传统 Java I/O 的 API,能够更高效地处理大量并发连接和数据传输。本文将详细介绍 Java NIO 中的核心类,包括 Channel、ByteBuffer 和 Selector 的使用方法和注意点。

一、Java NIO 概述

Java NIO 由以下几个核心部分组成:
Channels(通道):用于在字节缓冲区和 I/O 源(如文件、网络套接字)之间进行数据传输。
Buffers(缓冲区):用于存储数据的容器,可以是字节缓冲区、字符缓冲区等。
Selectors(选择器):用于监控多个通道的事件,实现单线程处理多个连接。

二、Channel(通道)

(一)Channel 简介

Channel 是对传统 I/O 中输入流和输出流的抽象,它代表一个到实体(如文件、网络套接字)的开放连接,可以进行读写操作。

(二)Channel 的类型

FileChannel:用于文件的读写操作。
SocketChannel:用于 TCP 网络连接的客户端。
ServerSocketChannel:用于 TCP 网络连接的服务器端,用于监听新的连接请求。
DatagramChannel:用于 UDP 网络通信。

(三)Channel 的创建和使用

创建 FileChannel
通过FileInputStream、FileOutputStream或RandomAccessFile获取FileChannel。

FileInputStream fis = new FileInputStream("file.txt");
FileChannel fileChannel = fis.getChannel();

创建 SocketChannel 和 ServerSocketChannel
创建客户端SocketChannel:

SocketChannel socketChannel = SocketChannel.open();
socketChannel.connect(new InetSocketAddress("localhost", 8080));

创建服务器端ServerSocketChannel:

ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.bind(new InetSocketAddress(8080));

使用 Channel 进行读写操作
从通道读取数据到缓冲区:

ByteBuffer buffer = ByteBuffer.allocate(1024);
int bytesRead = channel.read(buffer);

将缓冲区的数据写入通道:

buffer.flip();
int bytesWritten = channel.write(buffer);

(四)Channel 注意点

非阻塞模式:可以设置通道为非阻塞模式,在非阻塞模式下,通道的读写操作不会阻塞线程,而是立即返回。这对于处理大量并发连接非常有用。

channel.configureBlocking(false);

通道之间的数据传输:可以直接在两个通道之间传输数据,而无需通过中间缓冲区。

FileChannel sourceChannel =...;
FileChannel destinationChannel =...;
sourceChannel.transferTo(0, sourceChannel.size(), destinationChannel);

三、ByteBuffer(字节缓冲区)

(一)ByteBuffer 简介

ByteBuffer 是 Java NIO 中用于处理字节数据的缓冲区。它提供了一种高效的方式来存储和操作字节数据,可以与通道一起使用进行数据传输。

(二)ByteBuffer 的创建

分配指定大小的缓冲区

ByteBuffer buffer = ByteBuffer.allocate(1024);

包装现有的字节数组

byte[] array = new byte[1024];
ByteBuffer buffer = ByteBuffer.wrap(array);

(三)ByteBuffer 的基本操作

写入数据

buffer.put((byte)1);
buffer.put((byte)2);
buffer.put((byte)3);

读取数据

byte firstByte = buffer.get();
byte secondByte = buffer.get();

切换缓冲区模式
写入模式切换到读取模式:

buffer.flip();

读取模式切换到写入模式:

buffer.clear();

标记和重置
设置标记:

buffer.mark();

重置到标记位置:

buffer.reset();

获取缓冲区信息
获取当前位置:

int position = buffer.position();

获取容量:

int capacity = buffer.capacity();

获取限制:

int limit = buffer.limit();

(四)ByteBuffer 注意点

缓冲区的状态管理
位置(position):表示下一个要读取或写入的字节位置。
限制(limit):在读取模式下,限制表示可以读取的最大字节数;在写入模式下,限制通常等于缓冲区的容量。
容量(capacity):缓冲区的固定大小,一旦创建就不能改变。
字节顺序
ByteBuffer可以使用大端字节序(Big Endian)或小端字节序(Little Endian)。可以通过ByteBuffer.order()方法设置字节序。

ByteBuffer buffer = ByteBuffer.allocate(4);
buffer.order(ByteOrder.BIG_ENDIAN);
buffer.putInt(12345);

直接缓冲区和非直接缓冲区
直接缓冲区:通过ByteBuffer.allocateDirect()方法创建。直接缓冲区直接与操作系统的底层内存交互,通常在进行大量数据传输时性能更好。
非直接缓冲区:通过ByteBuffer.allocate()方法创建。非直接缓冲区在 Java 堆内存中分配,可能在与通道进行数据传输时需要额外的复制操作。
缓冲区的复用
可以重复使用同一个缓冲区来处理不同的数据块,通过clear()或compact()方法来准备缓冲区进行新的数据操作。

四、Selector(选择器)

(一)Selector 简介

Selector 是 Java NIO 中用于实现多路复用的关键组件。它可以同时监控多个通道的事件,当有事件发生时,选择器会通知相应的线程进行处理。

(二)Selector 的创建和使用

创建 Selector

Selector selector = Selector.open();

注册通道到 Selector
对于ServerSocketChannel:

ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.bind(new InetSocketAddress(8080));
serverSocketChannel.configureBlocking(false);
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

对于SocketChannel:

SocketChannel socketChannel = SocketChannel.open();
socketChannel.configureBlocking(false);
socketChannel.register(selector, SelectionKey.OP_READ);

使用 Selector 进行事件处理
选择就绪的通道:

int readyChannels = selector.select();

遍历就绪的通道:

Set<SelectionKey> selectedKeys = selector.selectedKeys();
Iterator<SelectionKey> keyIterator = selectedKeys.iterator();
while (keyIterator.hasNext()) {
    SelectionKey key = keyIterator.next();
    if (key.isAcceptable()) {
        // 处理连接接受事件
    } else if (key.isReadable()) {
        // 处理可读事件
    } else if (key.isWritable()) {
        // 处理可写事件
    }
    keyIterator.remove();
}

(三)Selector 注意点

事件类型
SelectionKey.OP_ACCEPT:连接接受事件,表示有新的连接请求。
SelectionKey.OP_CONNECT:连接完成事件,表示客户端连接已经建立。
SelectionKey.OP_READ:可读事件,表示通道中有数据可读。
SelectionKey.OP_WRITE:可写事件,表示通道可以写入数据。
非阻塞模式:与通道一样,选择器也在非阻塞模式下工作。这意味着select()方法不会阻塞线程,而是立即返回就绪的通道数量。
优化和性能考虑
避免频繁的select()调用,可以设置一个超时时间来控制等待事件的时间。
合理地处理事件,避免在事件处理中进行耗时的操作,以免影响其他通道的响应时间。

五、Java NIO 的应用场景

高并发网络服务器:可以使用 Java NIO 实现高性能的网络服务器,处理大量并发连接。
文件传输:利用通道和缓冲区可以高效地进行文件的读写和传输。
异步 I/O 操作:通过非阻塞模式和选择器,可以实现异步的 I/O 操作,提高程序的响应性。

目前Netty 等网络框架封装了复杂API的使用,开箱即用。

上一篇 下一篇

猜你喜欢

热点阅读