Java NIO原理机制详解

news/2024/11/17 12:42:24/

Java NIO原理机制

什么是NIO

Java NIO(New IO)是Java 1.4版本引入的一个新的IO API,与传统的Java IO有着同样的作用和目的,但是使用方式完全不同。NIO支持面向缓冲区的、基于通道的IO操作,可以提供比传统IO更高效、更灵活的IO操作方式,适用于高并发、大吞吐量的场景。

相比于传统的流式IO,NIO利用了基于缓存区的思路,将数据先读取到一个固定大小的缓存区中,然后再从缓存区中读取或写入数据,因此可以避免频繁地进行系统调用和上下文切换,从而提高了IO的效率。

NIO的实现原理

基本组件

Java NIO系统的核心在于通道(Channel)和缓冲区(Buffer)。通道表示打开到IO设备(如文件、套接字)的连接,缓冲区用于容纳数据进行处理。NIO的基本组件如下:

  • Channel:数据通道,类似于水管,用于读取和写入数据,可以理解为是一个双向的通道。
  • Buffer:缓存区,用于缓存数据,可以看成一块内存块,实际上是自动扩展的byte数组。
  • Selector:选择器,用于监听Channel上的IO事件,例如连接请求、数据到达等。

实现原理

NIO的实现原理主要基于以下几个方面:

缓存区(Buffer)

缓存区是NIO的一个核心组件,在缓存区中可以存储读取或写入的数据,缓冲区类别如下:

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

缓存区的操作主要包括两种:put和get。put操作将数据从外部写入缓冲区,get操作则将数据从缓冲区读取到外部。

通道(Channel)

通道表示打开到IO设备(如文件、套接字)的连接,可以读取和写入数据。通道主要分为两大类:

  • FileChannel:文件通道,用于文件的读写操作。
  • SocketChannel/ServerSocketChannel :网络通道,用于网络IO操作。

通道的读写操作主要通过Buffer进行,如下所示:

// 读取文件通道数据
FileInputStream fis = new FileInputStream("file.txt");
FileChannel fChannel = fis.getChannel();
ByteBuffer bBuf = ByteBuffer.allocate(1024);
int bytesRead = fChannel.read(bBuf); // 读取数据到缓冲区
while (bytesRead != -1) {System.out.println("Read " + bytesRead);bBuf.flip(); // 将缓冲区由写模式切换到读模式while (bBuf.hasRemaining()) {System.out.print((char) bBuf.get()); // 读取缓冲区数据}bBuf.clear(); // 清空缓冲区,准备下一次读取bytesRead = fChannel.read(bBuf);
}
fis.close();
// 写入文件通道数据
FileOutputStream fos = new FileOutputStream("file.txt");
FileChannel fChannel = fos.getChannel();
ByteBuffer bBuf = ByteBuffer.allocate(1024);
byte[] msgBytes = "Hello, NIO!".getBytes();
bBuf.put(msgBytes); // 将数据写入缓冲区
bBuf.flip(); // 将缓冲区由写模式切换到读模式
while (bBuf.hasRemaining()) {fChannel.write(bBuf); // 将缓冲区中的数据写入文件通道
}
fos.close();

选择器(Selector)

选择器是NIO提供的一种高效的IO多路复用机制,可以监听多个通道上的事件。在一个线程内可以同时监听多个通道的读就绪、写就绪等事件,有效地降低了多线程开发复杂度和系统开销。

选择器的基本操作如下:

  1. 调用Selector.open()创建一个Selector对象。
  2. 调用Channel.register()方法将通道注册到Selector上,并指定所需监听的IO事件。
  3. 不断调用Selector.select()方法进行选择操作,该方法会阻塞直到有对应的IO事件发生。
  4. 调用Selector.selectedKeys()方法获取Selector上已选择的IO事件集合。
  5. 通过遍历已选择的IO事件集合,处理对应的IO操作。
// 创建选择器
Selector selector = Selector.open();
// 注册通道到选择器,并指定监听事件
channel.configureBlocking(false);
SelectionKey key = channel.register(selector, SelectionKey.OP_READ);
// 不断循环选择操作
while (true) {int readyChannels = selector.select();if (readyChannels == 0) {continue;}Set<SelectionKey> selectedKeys = selector.selectedKeys();Iterator<SelectionKey> keyIterator = selectedKeys.iterator();while (keyIterator.hasNext()) {SelectionKey key = keyIterator.next();if (key.isAcceptable()) {// 处理连接接受事件} else if (key.isConnectable()) {// 处理连接就绪事件} else if (key.isReadable()) {// 处理读就绪事件} else if (key.isWritable()) {// 处理写就绪事件}keyIterator.remove();}
}

NIO的应用场景

NIO适用于大量连接并发、数据传输量较大的场景,例如:

  • 高并发的网络服务器,如Web服务器。
  • 分布式系统,如Hadoop等。
  • 数据库连接池,提高数据库操作效率。
  • 大文件的读写,通过内存映射的方式可以避免频繁的磁盘IO操作。

NIO实操案例

下面我们来看一个简单的NIO应用实例:基于NIO实现的Echo服务器。

服务器端代码

public class NIOServer {public static void main(String[] args) throws IOException {// 创建ServerSocketChannelServerSocketChannel serverSocketChannel = ServerSocketChannel.open();serverSocketChannel.socket().bind(new InetSocketAddress(8888));serverSocketChannel.configureBlocking(false);// 创建SelectorSelector selector = Selector.open();serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);while (true) {int readyChannels = selector.select();if (readyChannels == 0) {continue;}Set<SelectionKey> selectedKeys = selector.selectedKeys();Iterator<SelectionKey> keyIterator = selectedKeys.iterator();while (keyIterator.hasNext()) {SelectionKey key = keyIterator.next();if (key.isAcceptable()) {// 处理连接请求事件SocketChannel socketChannel = serverSocketChannel.accept();socketChannel.configureBlocking(false);socketChannel.register(selector, SelectionKey.OP_READ);System.out.println("建立新连接: " + socketChannel.getRemoteAddress());} else if (key.isReadable()) {// 处理读取数据事件SocketChannel socketChannel = (SocketChannel) key.channel();ByteBuffer buffer = ByteBuffer.allocate(1024);socketChannel.read(buffer);buffer.flip();socketChannel.write(buffer);}keyIterator.remove();}}}
}

客户端代码

public class NIOClient {public static void main(String[] args) throws IOException {SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("localhost", 8888));ByteBuffer buffer = ByteBuffer.allocate(1024);buffer.put("Hello, NIO!".getBytes());buffer.flip();socketChannel.write(buffer);buffer.clear();socketChannel.read(buffer);System.out.println(new String(buffer.array()));socketChannel.close();}
}

NIO总结

Java NIO是一种面向缓存区的、基于通道的IO操作方式,可以提供比传统IO更高效、更灵活的IO操作方式,适用于高并发、大吞吐量的场景。NIO的核心组件包括通道(Channel)、缓冲区(Buffer)、选择器(Selector),NIO的实现原理主要基于这些组件之间的交互和协作。NIO的典型应用场景包括网络服务器、分布式系统、数据库连接池等。


http://www.ppmy.cn/news/53172.html

相关文章

Git(六):基本命令(2):复位、修改、分支合并与日志

目录 9、reset 复位 9.1 描述 9.2 基本用法 9.2.1 回滚添加操作 9.2.2 回滚最近一次提交 9.2.3 回滚最近几次提交 9.2.4 回滚 pull 9.2.5 回滚 merge 9.2.6 区别 9.2.7 中断的工作流程处理 9.2.8 重置单独的文件 9.2.9 保留工作区并丢弃之前的提交 10、rm 删除 1…

本地 WAF 已死,云 WAF 永生

多年来&#xff0c;Web 应用程序防火墙 (WAF) 一直是应用程序保护的代名词。事实上&#xff0c;许多应用程序安全团队认为保护其应用程序的最佳选择是一流的本地 WAF 解决方案&#xff0c;尤其是当这些应用程序部署在本地或私有云中时。 但自从引入本地 WAF 以来&#xff0c;…

【大数据之Hadoop】二十二、Yarn调度器和调度算法

Hadoop作业调度器主要有三种&#xff1a;FIFO、容量&#xff08;Capacity Scheduler&#xff09;和公平&#xff08;Fair Scheduler&#xff09;。 Apache Hadoop默认的资源调度器&#xff1a;容量调度器Capacity Scheduler。 CDH框架默认调度器是Fair Scheduler。 1 FIFO 单…

多线程、智能指针以及工厂模式

目录 一、unique_lock 二、智能指针 &#xff08;其实是一个类&#xff09; 三、工厂模式 一、unique_lock 参考文章【1】&#xff0c;了解unique_lock与lock_guard的区别。 总结:unique_lock使用起来要比lock_guard更灵活&#xff0c;但是效率会第一点&#xff0c;内存的…

如何正确高效地学习android开发?

每一个能成为行业大佬的人&#xff0c;一定有自己独特的方法… 之所以能成为大佬&#xff0c;是因为他们会有自己独特的见解&#xff0c;在一次次的尝试中不断否定&#xff0c;然后一次次的确定&#xff0c;一个程序员想要精益求精&#xff0c;必须要有高效的学习方法和良好的…

iOS App启动流程

名称 区别 冷启动 启动时&#xff0c;App的进程不在系统里&#xff0c;需要开启新进程。 热启动 启动时&#xff0c;App的进程还在系统里&#xff0c;不需要开启新进程。 主要分为三个阶段&#xff1a; main() 函数执行前&#xff08;pre-main阶段&#xff09;main() 函…

深眸科技|深度学习、3D视觉融入机器视觉系统,实现生产数智化

随着“中国制造2025”战略加速落实&#xff0c;制造业生产线正在加紧向智能化、自动化和数字化转型之路迈进。而人工智能技术的兴起以及边缘算力持续提升的同时&#xff0c;机器视觉及其相关技术也在飞速发展&#xff0c;并不断渗透进工业领域&#xff0c;拓展应用场景的同时&a…

为了写markdown文件,Windows下的noevim配置

vim安装插件 在网页上写CSDN文章有诸多不便&#xff0c;感觉vim很好用&#xff0c;可是vim中没有预览功能。按照网上找到的教程安装插件&#xff0c;将以下代码复制的到_vimrc文件中。 set rtp$VIM/vimfiles/bundle/Vundle.vim/ call vundle#begin(./vimfiles/bundle/)"…