查看“NIO”的源代码
←
NIO
跳到导航
跳到搜索
因为以下原因,您没有权限编辑本页:
您所请求的操作仅限于该用户组的用户使用:
用户
您可以查看和复制此页面的源代码。
在 NIO(Nonblocking IO,非阻塞 IO)出现之前,Java 是通过传统的 Socket 来实现基本的网络通信功能的。 以服务端为例,其实现基本流程如图所示。 [[文件:Socket 使用流程.png|无|缩略图]]如果客户端还没有对服务端发起连接请求,那么 accept 就会阻塞[阻塞指的是暂停一个线程的执行以等待某个条件发生(例如某资源就绪)]。 如果连接成功,当数据还没有准备好的时候,对 read 的调用同样会阻塞。 当要处理多个连接的时候,就需要采用多线程的方式,由于每个线程都拥有自己的栈空间,而且由于阻塞会导致大量线程进行上下文切换,使得程序的运行效率非常低下。 因此在 J2SE 1.4 中引入了 NIO 来解决这个问题。 NIO 通过 Selector、Channels 和 Buffers 来实现非阻塞的 IO 操作。 NIO 是指 New I/O,既然有 New I/O,那么就会有 Old I/O,Old I/O 是指基于流的 I/O 方法。 NIO 是在 Java 1.4 中被纳入 JDK 中的,它最主要的特点是,提供了基于 Selector 的异步网络 I/O,使得一个线程可以管理多个连接。 基于 NIO 处理多个连接的结构图: [[文件:NIO 结构图.png|无|缩略图|551x551像素]]在介绍 NIO 的原理之前,首先介绍几个重要的概念:Channel(通道)、Buffer(缓冲区)和 Selector(选择器)。 ==== Channel(通道) ==== 为了更容易地理解什么是 Channel,这里以 InputStream 为例来介绍什么是 Channel。 传统的 IO 中经常使用下面的代码来读取文件:<syntaxhighlight lang="java"> import java.io.*; public class NIOTest { public static void main(String[] args) throws IOException { File file = new File("input.txt"); InputStream is = new FileInputStream(file); byte[] b = new byte[1024]; int read = 0; while ((read = is.read(b)) != -1) { //处理读取到的数据 } is.close(); } } </syntaxhighlight>InputStream 其实就是一个用来读取文件的通道。只不过 InputStream 是一个单向的通道,只能用来读取数据。 而 NIO 中的 Channel 是一个双向的通道,不仅能读取数据,而且还能写入数据。 ==== Buffer(缓冲区) ==== 在上面的示例代码中,InputStream 把读取到的数据放在了 byte 数组中,如果用 OutputStream 写数据,那么也可以把 byte 数组中的数据写到文件中。 而在 NIO 中,数据只能被写到 Buffer 中,同理,读取的数据也只能放在 Buffer 中,由此可见 Buffer 是 Channel 用来读写数据的非常重要的一个工具。 ==== Selector(选择器) ==== Selector 是 NIO 中最重要的部分,是实现一个线程管理多个连接的关键,它的作用就是轮询所有被注册的 Channel,一旦发现 Channel 上被注册的事件发生,就可以对这个事件进行处理。 === Buffer === 在 Java NIO 中,Buffer 主要的作用就是与 Channel 进行交互。 它本质上是一块可读写数据的内存,这块内存中有很多可以存储 byte、int、char 等的小单元。 这块内存被包装成 NIO Buffer 对象,并提供了一组方法,来简化数据的读写。 在 Java NIO 中,核心的 Buffer 有7类,如图: [[文件:Buffer 的类图.png|无|缩略图|947x947像素|Buffer 的类图]] 下面重点介绍 Buffer 中几个非常重要的属性:capacity、position 和 limit。 1)capacity 用来表示 Buffer 的容量,也就是刚开始申请的 Buffer 的大小。 2)position 表示下一次读(写)的位置。 在写数据到 Buffer 中时,position 表示当前可写的位置。初始的 position 值为0。 当写入一个数据(例如 int 或 short)到 Buffer 后,position 会向前移动到下一个可插入数据的 Buffer 单元。 position 最大的值为 capacity - 1。 在读取数据时,也是从某个位置开始读。当从 Buffer 的 position 处读取数据完成时,position 也会从向前位置移动到下一个可读的位置。 buffer 从写入模式变为读取模式时,position 会归零,每次读取后,position 向后移动。 3)limit 表示本次读(写)的极限位置。 在写入数据时,limit 表示最多能往 Buffer 里写入多少数据,它等同于 buffer 的容量。 在读取数据时,limit 表示最多能读到多少数据,也就是说 position 移动到 limit 时读操作会停止。它的值等同于写模式下 position 的位置。 为了更容易地理解这三个属性之间的关系,用下图来说明: [[文件:Buffer 的内部原理.png|无|缩略图|756x756像素]] 在理解了 Buffer 的内部实现原理后,下面重点介绍如何使用 Buffer。 (1)申请 Buffer 在使用 Buffer 前必须先申请一块固定大小的内存空间来供 Buffer 使用,这个工作可以通过 Buffer 类提供的 allocate() 方法来实现。
返回至
NIO
。
导航菜单
个人工具
登录
名字空间
页面
讨论
变种
视图
阅读
查看源代码
查看历史
更多
搜索
导航
首页
Spring Boot 2 零基础入门
Spring Cloud
Spring Boot
设计模式之禅
VUE
Vuex
Maven
算法
技能树
Wireshark
IntelliJ IDEA
ElasticSearch
VirtualBox
软考
正则表达式
程序员精讲
软件设计师精讲
初级程序员 历年真题
C
SQL
Java
FFmpeg
Redis
Kafka
MySQL
Spring
Docker
JMeter
Apache
Linux
Windows
Git
ZooKeeper
设计模式
Python
MyBatis
软件
数学
PHP
IntelliJ IDEA
CS基础知识
网络
项目
未分类
MediaWiki
镜像
问题
健身
国债
英语
烹饪
常见术语
MediaWiki帮助
工具
链入页面
相关更改
特殊页面
页面信息