Java NIO 概述(Channel、Buffer、Selector)

简介: Java NIO (New IO 或 Non Bloking IO) 是从 Java 1.4 版本开始引入一个新的 IO API, 可以替代标准的 Java IO API。NIO 全面支持面向缓冲区的、基于通道的 IO 操作。NIO 将以高效的方式进行文件的读写操作。

阻塞 IO


通常在进行同步 I/O 操作时,如果读取数据,代码会阻塞直至有可供读取的数据。同样,写入调用将会阻塞直至数据能够写入。传统的 Server/Client 模式会基于 TPR (Thread per Request ), 服务器会为每个客户端请求建立一个线程,由该线程单独负责处理一个客户请求。这种模式带来的问题就是线程数量的急剧增加,大量的线程会增大服务的开销。大多数的实现为了避免这个问题,采用了线程池模型,并设置线程池的最大数量,这个带来了新的问题,如果线程池中有 100 个线程,而有 100个用户都在进行大文件下载,会导致 101 个用户的请求无法及时处理,即便第 101 个用户指向请求一个几 kb 大小的页面。传统的 Server /Client 模式如下图所示


image.png


非阻塞 IO(NIO)


NIO 中非阻塞 I/O 采用了基于 Reactor 模式的工作方式, I/O 调用不会被阻塞, 相反是注册感兴趣的 I/O 事件,如可读数据到达,新的套接字连接等等,在发生特定的事件时,系统再通知我们。NIO 中实现非阻塞 I/O 的核心对象就是 Selector 。


Selector 就是注册各种 I / O 时间地方,而且当我们感兴趣的事件发生时,就是这个对象告诉我们发生的事件,如下图所示:


image.png


从图中可以看到,当有读,或者写等任何注册事件发生时,可以从 Selector 中获取响应的 SelectionKey , 同事从 SelectorKey 中恶意找到发生的事件和该事件所发生的具体 SelectableCannel 以获取客户端发送过来的数据。


非阻塞指的是 IO 本身不阻塞,但是获取 IO 事件的 select() 方法是需要阻塞等待的。区别阻塞的 IO 会阻塞在 IO 操作上,NIO 阻塞在事件获取上,没有事件就没有 IO , 从高层次来看 IO 就不阻塞了,也就是说只有 IO 已经发生那么我们才评估 IO 是否阻塞,但是 select() 阻塞的时候 IO 还没有发生,何谈 IO 的阻塞呢? NIO的本质是延迟 IO 操作到真正发生 IO的时候,而不是以前只要 IO 流打开就一直等待 IO 操作。


总结:NIO 不会发生 IO 打开后阻塞,而是在 IO 发生的时候会处理,其实本质也是一种 IO 延迟阻塞。


IO NIO
面向流(Stream Oriented) 面向缓冲区(Buffer Oriented)
阻塞IO(Blocking IO) 非阻塞IO(Non Blocking IO)
(无) 选择器(Selector)


NIO 概述


Java NIO 由一下几个核心部分组成:


  • Channels


  • Buffers


  • Selectors


虽然 Java NIO 中除此之外还有很多类和组件,单 Channel, Buffer 和 Selector 构成了核心的 API 。其他组件,如 Pipe 和 FileLock , 只不过是与三个核心组件共同协作的工具类。


Channel


首先说下 Channel ,可以翻译为 “通道”Channel 和 IO 的Stream (流)是差不多的一个等级的。只是 Stream 是单向的,比如 InputStream , OutputStream. 而 Channel 是双向的,既可以用来进行读操作,又可以用来进行写操作。


NIO 中的 Channel 的主要实现有:FileChannel、DatagramChannel 、SocketChannel 和 ServerSocketChannel , 这里看名字就可以猜出来: 分别对应文件 IO 、UDP 和 TCP (Server 和 Client)。


Buffer


NIO 找那个关键 Buffer 实现有:ByteBuffer , CharBuffer , DoubleBuffer , FloatBuffer, IntBuffer, LongBuffer, ShortBUffer , 分别对应基本数据类型 : byte , char , dubbole, float , int , long , short.


Selector


Selector 运行单线程处理多个 Channel , 如果你的引用中开启了多个通道,但是每个连接的流量都很低,使用 Selector 就会很方便,例如在一个聊天服务器中,要使用 Selector, 需要向 Selector 中注册 Channel , 然后调用 select() 方法。 这个方法一直会阻塞到其注册的通道有事件就绪。一旦这个方法返回,线程就可以处理这些事件,事件的例子有:新的连接进来、数据接收等。


相关文章
|
Java
让星星⭐月亮告诉你,Java NIO之Buffer详解 属性capacity/position/limit/mark 方法put(X)/get()/flip()/compact()/clear()
这段代码演示了Java NIO中`ByteBuffer`的基本操作,包括分配、写入、翻转、读取、压缩和清空缓冲区。通过示例展示了`position`、`limit`和`mark`属性的变化过程,帮助理解缓冲区的工作原理。
284 2
|
缓存 Java
java文件读取 while ((len = reader.read(buffer)) != -1){}的理解
本文解释了Java中使用`InputStreamReader`和`read(buffer)`方法循环读取文件内容的机制,强调了如何正确理解读取循环和处理读取到的数据,以及如何处理字符编码和换行符。
413 0
|
存储 编解码 移动开发
技术笔记:NIO流—理解Buffer、Channel概念和NIO的读写操作
技术笔记:NIO流—理解Buffer、Channel概念和NIO的读写操作
275 1
Java NIO Selector
Java NIO Selector
252 0
|
Java 索引
📌 Java NIO Buffer
Java NIO缓冲区在与NIO通道交互时使用。数据从通道读取到缓冲区,然后从缓冲区写入通道。 缓冲区本质上是一块内存,可以在其中写入数据,然后再进行读取。这个内存块被封装在一个NIOBuffer对象中,该对象提供了一组方法,可以更容易地使用内存块。
|
缓存 网络协议 Java
📌 Java NIO Channel
Java NIOChannel和传统的流相似,但是也存在一些差异: • 在同一个Channel通道中,既可以进行 读操作 也可以进行 写操作,但是 流 只能进行 读 或者 写 其中一种操作。 • Channel通道可以进行异步读写。 • Channel可以从 Buffer中进行读写操作。将数据从Channel通道读取到Buffer缓冲区,并将数据从Buffer缓冲区写入Channel通道。
|
9月前
|
JSON 网络协议 安全
【Java】(10)进程与线程的关系、Tread类;讲解基本线程安全、网络编程内容;JSON序列化与反序列化
几乎所有的操作系统都支持进程的概念,进程是处于运行过程中的程序,并且具有一定的独立功能,进程是系统进行资源分配和调度的一个独立单位一般而言,进程包含如下三个特征。独立性动态性并发性。
427 1
|
9月前
|
JSON 网络协议 安全
【Java基础】(1)进程与线程的关系、Tread类;讲解基本线程安全、网络编程内容;JSON序列化与反序列化
几乎所有的操作系统都支持进程的概念,进程是处于运行过程中的程序,并且具有一定的独立功能,进程是系统进行资源分配和调度的一个独立单位一般而言,进程包含如下三个特征。独立性动态性并发性。
394 1
|
10月前
|
数据采集 存储 弹性计算
高并发Java爬虫的瓶颈分析与动态线程优化方案
高并发Java爬虫的瓶颈分析与动态线程优化方案
Java 数据库 Spring
434 0