Java NIO技术总结
一、背景
大家都知道Java BIO,其全称是java blocking IO,相对的Java NIO 全称为java non-blocking IO。顾名思义,java nio 是一种非阻塞IO。NIO是为了弥补IO操作的不足而诞生的,NIO的一些新特性有:非阻塞I/O,选择器,缓冲以及管道。管道(Channel),缓冲(Buffer) ,选择器( Selector)是其主要特征。提供基于缓冲区(buffer)的块写入/读取,而以前的I/O是基于流(Stream)的方式,NIO基于块的IO操作,将最耗时的缓存区读取和填充交由底层操作系统实现,因此速度上要快得多。
二、NIO的名词解释
1、Buffer
Buffer是一个对象,它用来存放即将发送的数据和即将到来的数据。发送给一个通道的所有对象都必须首先放到缓冲区中;同样地,从通道中读取的任何数据都要读到缓冲区中。Buffer是NIO核心思想,它与普通流IO的区别是,普通流IO直接把数据写入或读取到Stream对象中,而NIO是先把读写数据交给Buffer,后在用流处理的。Buffer实际上就是一个数组,通常是字节数组,但是这个数组提供了访问数据的读写等操作属性,如位置,容量,上限等概念。
在NIO中,Buffer是一个顶层父类,它是一个抽象类,常用的Buffer的子类有:ByteBuffer、IntBuffer、CharBuffer、LongBuffer、DoubleBuffer、FloatBuffer、ShortBuffer。如果是对于文件读写,上面几种Buffer都可能会用到。但是对于网络读写来说,用的最多的是ByteBuffer。
前面提到,buffer实际上是一个封装的字节数组,有两个重要组件:状态变量和访问方法。而buffer提供的访问数据的读写等操作属性,如位置,容量,上限等概念是依靠状态变量实现的。可以用三个值指定缓冲区在任意时刻的状态:
- position:跟踪已经写了多少数据。
position
总是小于或者等于limit
。 - limit:表明还有多少数据需要取出(在从缓冲区写入通道时),或者还有多少空间可以放入数据(在从通道读入缓冲区时)。limit 决不能大于 capacity。
- capacity:表明可以储存在缓冲区中的最大数据容量。实际上,它指定了底层数组的大小 ― 或者至少是指定了准许我们使用的底层数组的容量。
- position:跟踪已经写了多少数据。
向Buffer中写数据有两种方式:
(1) 从Channel写到Buffer。如:int bytesRead = inChannel.read(buf); //read into buffer.
(2) 通过Buffer的put()方法写到Buffer里。如:buf.put(127);
从Buffer中读取数据也有两种方式:
(1)从Buffer读取数据到Channel。如: int bytesWritten = inChannel.write(buf);
(2)使用get()方法从Buffer中读取数据。如:byte aByte = buf.get();
2、Channel (通道)
与Stream(流)的不同之处在于通道是双向的,流只能在一个方向上操作(一个流必须是InputStream或者OutputStream的子类),而通道可以用于读,写或者二者同时进行,最关键的是可以和多路复用器结合起来,提供状态位,多路复用器可识别Channel所处的状态。
通道可以分两大类:用于网络读写的SelectableChannel,和用于文件操作的FileChannel。具体来说:通过FileChannel可以从文件读或者向文件写入数据;通过SocketChannel,以TCP来向网络连接的两端读写数据;通过ServerSocketChanel能够监听客户端发起的TCP连接,并为每个TCP连接创建一个新的SocketChannel来进行数据读写;通过DatagramChannel,以UDP协议来向网络连接的两端读写数据。
3、Selector
Selector提供选择已经就绪的任务的能力。简单说,就是Selector会不断轮询注册在Selector上的通道(Channel),如果这个通道发生了读写操作,这个通道就会处于就绪状态,会被Selector察觉到,然后通过SelectionKey可以取出就绪的Channel集合,从而进行IO操作。
一个Selector可以负责成千上万的通道,没有上限。这也是JDK使用了epoll代替传统的Select实现,获得连接句柄没有限制。意味着我们只需要一个线程负责Selector的轮询,就可以接入成百上千的客户端,这是JDK NIO库的巨大进步。
Selector类是NIO的核心类,Selector能够检测多个注册的通道上是否有事件发生,如果有事件发生,便获取事件然后针对每个事件进行相应的响应处理。这样一来,只是用一个单线程就可以管理多个通道,也就是管理多个连接。这样使得只有在连接真正有读写事件发生时,才会调用函数来进行读写,就大大地减少了系统开销,并且不必为每个连接都创建一个线程,不用去维护多个线程,并且避免了多线程之间的上下文切换导致的开销。
与Selector有关的一个关键类是SelectionKey,一个SelectionKey表示一个到达的事件,这2个类构成了服务端处理业务的关键逻辑。
三、标准的NIO步骤
一个简单(标准)的NIO输入输出一般包含如下步骤:
1. 从数据源获取通道 ;
2. 分配缓冲区 ;
3. 切换缓存区为写模式;
4. 从通道读取数据写入缓冲区;
5. 切换缓冲区为读模式 ;
6. 缓冲区数据写入通道中 ;
7. 关闭资源。
package com.denny.aio.test; import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel; public class Test { public static void main(String[] args) throws IOException { RandomAccessFile formFile = new RandomAccessFile("src\\a.txt", "rw");
RandomAccessFile toFile = new RandomAccessFile("src\\b.txt", "rw"); //获取channel
FileChannel fromChannel = formFile.getChannel();
FileChannel toChannel = toFile.getChannel(); // 定义缓冲大小
int bufSize = 1024*4; // 定义缓冲
ByteBuffer byteBuffer = ByteBuffer.allocate(bufSize); int len = 0; // 将数据从源channel写入到缓冲区
while( (len=fromChannel.read(byteBuffer)) !=-1 ){ //切换到读模式
byteBuffer.flip(); //读取缓冲区数据写到目标channel
toChannel.write(byteBuffer); // 清空缓冲
byteBuffer.clear();
} // 释放资源
toChannel.close();
fromChannel.close();
}
}
注意:
将数据写到输出通道中。在这之前,我们必须调用 flip() 方法。这个方法做两件非常重要的事:
(1) 它将 limit 设置为当前 position。这意味着它包括以前读到的所有字节,并且一个字节也不多。
(2)它将 position 设置为 0。这意味着我们得到的下一个字节是第一个字节。
最后一步是调用缓冲区的 clear() 方法。这个方法重设缓冲区以便接收更多的字节。 Clear 做两种非常重要的事情:
(1)它将 limit 设置为与 capacity 相同。
(2)它设置 position 为 0。
四、NIO和传统的IO区别
1,IO是面向流的,NIO是面向块(缓冲区)的。
IO面向流的操作一次一个字节地处理数据。一个输入流产生一个字节的数据,一个输出流消费一个字节的数据。,导致了数据的读取和写入效率不佳;
NIO面向块的操作在一步中产生或者消费一个数据块。按块处理数据比按(流式的)字节处理数据要快得多,同时数据读取到一个它稍后处理的缓冲区,需要时可在缓冲区中前后移动。这就增加了处理过程中的灵活性。通俗来说,NIO采取了“预读”的方式,当你读取某一部分数据时,他就会猜测你下一步可能会读取的数据而预先缓冲下来。
2,IO是阻塞的,NIO是非阻塞的。
对于传统的IO,当一个线程调用read() 或 write()时,该线程被阻塞,直到有一些数据被读取,或数据完全写入。该线程在此期间不能再干任何事情了。
而对于NIO,使用一个线程发送读取数据请求,没有得到响应之前,线程是空闲的,此时线程可以去执行别的任务,而不是像IO中那样只能等待响应完成。而在NIO的非阻塞模式下,线程发送数据与接收数据都是通过“通道”进行的,线程只需要去询问通道是否有数据需要处理,有则处理,无则立即返回不会进行等待。线程通常将非阻塞IO的空闲时间用于处理其他通道上的IO事件,使用一个单独的线程就可以管理多个输入和输出通道。那么NIO是怎么实现非阻塞的呢?其实原理很简单,NIO是面向块的,先把数据搬运过来,存放到一个缓冲区中,线程过一段时间来缓冲区看看,有没有数据,这个样线程就不需要始终关注IO了。
3,NIO和IO适用场景
NIO是为弥补传统IO的不足而诞生的,但是尺有所短寸有所长,NIO也有缺点,因为NIO是面向缓冲区的操作,每一次的数据处理都是对缓冲区进行的,那么就会有一个问题,在数据处理之前必须要判断缓冲区的数据是否完整或者已经读取完毕,如果没有,假设数据只读取了一部分,那么对不完整的数据处理没有任何意义。所以每次数据处理之前都要检测缓冲区数据。
那么NIO和IO各适用的场景是什么呢?
如果需要管理同时打开的成千上万个连接,这些连接每次只是发送少量的数据,例如聊天服务器,这时候用NIO处理数据可能是个很好的选择。
而如果只有少量的连接,而这些连接每次要发送大量的数据,这时候传统的IO更合适。使用哪种处理数据,需要在数据的响应等待时间和检查缓冲区数据的时间上作比较来权衡选择。
对于NIO和传统IO,有一个网友讲的生动的例子:以前的流总是堵塞的,一个线程只要对它进行操作,其它操作就会被堵塞,也就相当于水管没有阀门,你伸手接水的时候,不管水到了没有,你就都只能耗在接水(流)上。nio的Channel的加入,相当于增加了水龙头(有阀门),虽然一个时刻也只能接一个水管的水,但依赖轮换策略,在水量不大的时候,各个水管里流出来的水,都可以得到妥善接纳,这个关键之处就是增加了一个接水工,也就是Selector,他负责协调,也就是看哪根水管有水了的话,在当前水管的水接到一定程度的时候,就切换一下:临时关上当前水龙头,试着打开另一个水龙头(看看有没有水)。当其他人需要用水的时候,不是直接去接水,而是事前提了一个水桶给接水工,这个水桶就是Buffer。也就是,其他人虽然也可能要等,但不会在现场等,而是回家等,可以做其它事去,水接满了,接水工会通知他们。
这其实也是非常接近当前社会分工细化的现实,也是统分利用现有资源达到并发效果的一种很经济的手段,而不是动不动就来个并行处理,虽然那样是最简单的,但也是最浪费资源的方式。
文中部分参考自:https://www.cnblogs.com/bootdo/p/10431789.html
Java NIO技术总结的更多相关文章
- Java NIO框架Mina、Netty、Grizzly介绍与对比(zz)
Mina:Mina(Multipurpose Infrastructure for Network Applications) 是 Apache 组织一个较新的项目,它为开发高性能和高可用性的网络应用 ...
- Java NIO框架Mina、Netty、Grizzly介绍与对比
Mina:Mina(Multipurpose Infrastructure for Network Applications) 是 Apache 组织一个较新的项目,它为开发高性能和高可用性的网络应用 ...
- (转)Java NIO框架Mina、Netty、Grizzly介绍与对比
转:http://blog.csdn.net/cankykong1/article/details/19937027 Mina: Mina(Multipurpose Infrastructure fo ...
- Java NIO的理解和应用
Java NIO是一种基于通道和缓冲区的I/O方式,已经被广泛的应用,成为解决高并发与大量连接和I/O处理问题的有效方式. Java NIO相关组件 Java NIO主要有三个核心部分组成,分别是:C ...
- Java NIO全面详解(看这篇就够了)
很多技术框架都使用NIO技术,学习和掌握Java NIO技术对于高性能.高并发网络的应用是非常关键的@mikechen NIO简介 NIO 中的 N 可以理解为 Non-blocking,不单纯是 N ...
- 支撑Java NIO 与 NodeJS的底层技术
支撑Java NIO 与 NodeJS的底层技术 众所周知在近几个版本的Java中增加了一些对Java NIO.NIO2的支持,与此同时NodeJS技术栈中最为人称道的优势之一就是其高性能IO,那么我 ...
- JAVA NIO 中的 zerocopy 技术提高IO性能
关于一篇更详细更好的介绍 ZeroCopy技术的文章,可参考:JAVA IO 以及 NIO 理解 这篇文章介绍了 zerocopy技术来提高Linux平台上的IO密集型的JAVA应用程序的性能. ze ...
- java基础-网络编程(Socket)技术选型入门之NIO技术
java基础-网络编程(Socket)技术选型入门之NIO技术 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.传统的网络编程 1>.编写socket通信的MyServer ...
- Java NIO浅析 转至 美团技术团队
出处: Java NIO浅析 NIO(Non-blocking I/O,在Java领域,也称为New I/O),是一种同步非阻塞的I/O模型,也是I/O多路复用的基础,已经被越来越多地应用到大型应用服 ...
随机推荐
- 架构设计之「 CAP 定理 」
在计算机领域,如果是初入行就算了,如果是多年的老码农还不懂 CAP 定理,那就真的说不过去了.CAP可是每一名技术架构师都必须掌握的基础原则啊. 现在只要是稍微大一点的互联网项目都是采用 分布式 结构 ...
- Docker进阶之七:管理应用程序数据
管理应用程序数据:Volume Docker提供三种不同的方式将数据从宿主机挂载到容器中:volumes,bind mounts和tmpfs. volumes:Docker管理宿主机文件系统的一部分( ...
- 痞子衡嵌入式:第一本Git命令教程(0)- 索引
大家好,我是痞子衡,是正经搞技术的痞子.本系列痞子衡给大家讲的是Git命令汇编,共12篇文章,循序渐进地介绍Git操作的完整过程. 在开始Git课程之前,需要先跟大家普及2个重要概念(四度空间.四种状 ...
- 关于CSS引入方式的详细见解
关于CSS的发展史这里不做介绍.写博客的原因之一是想帮助那些与我一样喜欢纠结的初入前端的伙伴,希望自己写的帖子能对伙伴有些许帮助:原因之二这些帖子也算自己的一个知识的整理.现在还没有一定的顺序可循,但 ...
- async/Await使用和原理
await/async是.NetFramework4.5出现的,是语法糖,由编译器提供的功能! await/async 是C#保留关键字,通常是成对出现,一般的建议是:要么不用,要么用到底 async ...
- 为Qt视图中的文字添加彩虹渐变效果
将view中的文本内容用自定义的颜色显示是一种十分常见的需求.今天我们稍微改变些"花样". 本文索引 需求定义 需求分析 代码实现 思考题 需求定义 我们的需求很简单,现在有一些在 ...
- c# 构造tree下拉框,空格转化
c#代码写的空格如何在html中的select中展示出来呢? var str = ""; //父级菜单不缩进 ; j < i; j++) { str += HttpUtili ...
- java实现 批量转换文件编码格式
一.场景说明 不知道大家有没有遇到过之前项目是GBK,现在需要全部换成UTF-8的情况.反正我是遇到了. eclipse可以改变项目的编码格式,但是文件如果直接转换的话里面的中文就会全部乱码,需要先复 ...
- 第三章:shiro授权认证
授权:也叫访问控制,即在应用中控制谁能访问哪些资源(如访问页面/编辑数据/页面操作等). 主体:即访问应用的用户,在Shiro中使用Subject代表该用户.用户只有授权后才允许访问相应的资源. 资源 ...
- MongoDB学习(管理数据库和集合)
管理数据库 显示数据库列表 show dbs 切换到其他数据库 use <database_name> 创建数据库 MongoDB没有提供显式的创建数据库的MongoDB shell命令. ...