NIO基础
1.三大组件
1.1. Channel & Buffer
Channel
在Java NIO(New I/O)中,“Channel”(通道)是一个重要的概念,用于在非阻塞I/O操作中进行数据的传输。Java NIO提供了一种更为灵活和高效的I/O处理方式,相比于传统的I/O,它具有更好的性能和可扩展性。
常见的Java NIO中的通道类型:
- FileChannel(文件通道):
- 用于文件I/O操作的通道,可以在文件中进行读取、写入和操作。
- SocketChannel(套接字通道):
- 用于TCP协议的通道,可以在网络套接字连接中进行读取和写入数据。(客户端,服务器端都能用)
- ServerSocketChannel(服务器套接字通道):
- 监听新的TCP连接的通道,当有新的连接进来时,可以接受并创建对应的SocketChannel。(只能用于服务器端)
- DatagramChannel(数据报通道):
- 用于UDP协议的通道,可以在网络中发送和接收数据报。
通过使用这些通道,Java NIO可以提供非阻塞的I/O操作,允许程序在一个线程中管理多个I/O操作,提高了系统的并发处理能力和性能表现。通道和缓冲区(Buffer)结合使用,能够实现更灵活、高效的数据传输和处理。
Buffer
在Java NIO中,Buffer(缓冲区)是一个核心概念,用于在通道(Channel)和数据源之间进行数据交互。Buffer实际上是一个容器,用于存储特定类型的数据,并提供了方便的方法来读取和写入数据。
常见的Java NIO中的Buffer类型:
-
ByteBuffer:
- ByteBuffer是最基本的缓冲区类型,用于存储字节数据。
- MappedByteBuffer:
- MappedByteBuffer是ByteBuffer的一个子类,它通过文件通道直接映射到内存中,可以实现高效的文件I/O操作。
- MappedByteBuffer:
- ByteBuffer是最基本的缓冲区类型,用于存储字节数据。
-
CharBuffer:
- CharBuffer用于存储字符数据。
-
ShortBuffer、IntBuffer、LongBuffer、FloatBuffer、DoubleBuffer:
- 这些是用于存储各种基本数据类型的缓冲区,分别用于存储short、int、long、float和double类型的数据。
这些缓冲区提供了一种灵活的方式来管理数据,可以在缓冲区和通道之间进行数据传输,也可以在缓冲区内部进行数据处理。通过缓冲区,Java NIO实现了高效的I/O操作,可以在同一线程内处理多个通道,提高了系统的并发性能。
1.2.Selector
当使用 Java NIO 进行非阻塞 I/O 操作时,Selector(选择器)是一种关键的组件。它允许单个线程有效地管理多个 Channel,监视它们的状态并且在
至少一个 Channel 准备好进行读取、写入或者连接时
,唤醒线程。Selector 的作用是配置一个线程来管理多个 Channel,获取这些 Channel 上发生的事件。这些 Channel 通常工作在非阻塞模式下,因此不会让线程阻塞在一个 Channel 上。Selector 适用于连接数量特别多但流量低的场景,因为它可以有效地利用一个线程来处理多个连接,降低线程创建和上下文切换的开销。
- 非阻塞 I/O:
- Selector 允许程序使用单个线程处理多个 Channel,而不是为每个 Channel 都创建一个线程。这样可以显著减少线程的数量,提高系统的并发处理能力。
- Channel 注册:
- 在使用 Selector 之前,需要将每个 Channel 注册到 Selector 上,并指定感兴趣的事件类型,如读、写、连接等。
- 事件就绪集合:
- Selector 会不断轮询注册在其上的 Channel,当一个或多个 Channel 准备好进行 I/O 操作时,Selector 将返回一个就绪事件的集合。
- 事件驱动的编程模型:
- 应用程序通过监听就绪事件的方式,实现了一种事件驱动的编程模型,根据不同的事件类型执行相应的操作。
- 资源管理:
- 使用完 Selector 后,应及时关闭以释放资源,避免资源泄漏和性能问题。
通过 Selector,Java NIO 提供了一种高效的非阻塞 I/O 模型,使得应用程序能够更好地处理大量并发连接,提高系统的性能和可伸缩性。
2.ByteBuffer
ByteBuffer 是 Java NIO 中用于处理字节数据的缓冲区类之一。它提供了一种灵活的方式来处理字节数据,例如读取、写入、修改、复制等操作。
以下是关于 ByteBuffer 的一些重要特点和用法:
- 容量和位置:
- ByteBuffer 有一个固定的容量,表示它可以存储的最大字节数量。它还有一个位置(position),表示下一个读取或写入操作将要发生的位置。
- 读写操作:
- ByteBuffer 可以被用来进行读取和写入操作。它提供了诸如 put()、get()、putInt()、getInt() 等方法来读取和写入字节数据。
- 字节顺序(Byte Order):
- ByteBuffer 可以配置为使用不同的字节顺序,例如大端字节序(Big Endian)或小端字节序(Little Endian),以适应不同的数据格式。
- 直接缓冲区和非直接缓冲区:
- ByteBuffer 可以是直接缓冲区或非直接缓冲区。直接缓冲区使用操作系统的内存空间,通常性能更好,而非直接缓冲区使用 Java 虚拟机的堆内存。
- 清除、翻转和重绕:
- ByteBuffer 提供了一些方法来处理缓冲区的状态,例如 clear() 方法清除缓冲区中的数据(写模式)、flip() 方法翻转缓冲区以便读取已写入的数据(读模式)、rewind() 方法重绕缓冲区以便重新读取已读数据(写模式)。
- 其他操作:
- ByteBuffer 还提供了一些其他的操作,如 compact() 方法压缩缓冲区、slice() 方法创建一个新的缓冲区视图等。
ByteBuffer 在网络编程、文件 I/O、数据处理等