Java NIO的出现旨在提高文件的读写速度,当然IO用NIO重新实过,所以我们不用显示的调用NIO也能享受这种高效的文件读写。

  Java NIO的高效得益于其两大"助手":Channel(管道)和Buffer(缓冲器)。当然这两个"得力助手"的"年龄"远远比java大!力求简单易懂的把知识讲解给大家,我举一个例子来说明一下这"两元大将"是如何在java NIO中配合工作的。

  中国古代有一种传统的吸烟器具---水烟袋。我想用这个东西来模拟一下Channel和Buffer的工作原理。不求说的好,力求准确无误。

分析一下水烟袋是如何工作的:

  第一步,准备工作,准备好上等烟丝;第二步,将"水斗"中装入适量的水,烟仓中装满烟丝并插入水斗中,然后再将烟管插入水斗中;第三步,点燃烟丝并吸气。香烟从烟仓产生,经过水的过滤进入水上的空闲区。第四步,享受吸烟的快感.....从这个例子中我们提取出主要对象"烟",来分析一下它的运动轨迹。烟仓把烟生产出来,经过水的过滤飘到水上面的空闲区域,然后通过烟管进入人的体内。

  如果上面的过程大家理解了,明白了,那么java NIO你已经了解了50%,至少你已经知道它的工作原理了。因为用NIO处理的数据和用水烟袋中吸烟很相似。我们分析一下NIO的工作原理,非常简单。

  当然和吸烟一样我们首先必须有要用NIO来处理需求的欲望(这好比你想要吸烟了),比方说我想要将C盘下面的wk.txt文件进行备份,备份文件的名称为wk-bak.txt。类比刚刚吸烟的那个过程:

  步骤一:准备工作,确定文件的位置,并将程序不可直接操作的文件转换成字符流的形式(这一步和上边吸烟实例的第一步没有什么差别,只是进行一些简单的准备工作)。

String inFile = "C:\\wk.txt";
String outFile = "C:\\wk-bak.txt";
FileInputStream inf = new FileInputStream(inFile);
FileOutputStream outf = new FileOutputStream(outFile);
ByteBuffer buffer = ByteBuffer.allocate(1024);

  步骤二:创建文件输入管道,和文件输出管道。(这一步与上边吸烟的第二部稍有差别,因为Channel和Buffer是在读写的时候才发生的"连接"动作)

//准备文件读取的管道-->相当于烟仓和烟管
FileChannel inFc = inf.getChannel();
FileChannel outFc = outf.getChannel(); Charset charSet = Charset.forName("utf-8");
//进行编码解码-->相当于水斗中水的过滤作用
CharsetDecoder decoder = charSet.newDecoder();
CharsetEncoder encoder = charSet.newEncoder();  

  步骤三:开始进行文件备份工作。

       while(true) {
//准备向Buffer中写入数据-->相当于点燃烟丝,完事具备只欠东风
buffer.clear(); //进行字符编码 -->相当于水的过滤作用
CharBuffer cb = decoder.decode(buffer);
ByteBuffer bb = encoder.encode(cb); //数据经过编码以后暂存缓冲区-->相当于经过水过滤后的烟暂停在水斗中
int t = inFc.read(bb);
if(t == -1) {
break;
} bb.flip(); //将字节码写入目标文件-->相当于烟已经进入到嘴里
outFc.write(bb);
}

  步骤四:检查文件是否备份成功。发现C盘下面多了一个wk-bak.txt的文件,内容与wk.txt一摸一样。接下来享受java带给你的快感....

  上面的例子估计大家已经理解的差不多了,当然如果深究也会有一些不太妥当的地方,但是不要较真,目的是学习NIO,并不是吸烟。如果感觉你可以了那么就请把上面的例子补充完整,运行一下,享受一下NIO的威武(当然字符编码并不是必须的,只是让这个例子显得完整一点)。

  好吧如果你理解了上面的东西,并且真正的补全了文件备份的小程序,那么就来进行稍微深入一点的学习吧。

  上文我提到了举吸烟的例子是有欠妥当的,其中之一就是Buffer的内部机制和"水斗"简单的过滤功能是不一样的。还有字符编码那一块也不是在Buffer内部实现的东西,decoder和encoder是针对Buffer的两个工具。那我们接下来分析一下Buffer内部机制到底不一样在哪里呢(主要分析常用的两个方法;clear(),flip())?

  来吧,打开Buffer的源码(摘取有用的部分):

public abstract class Buffer {

    // Invariants: mark <= position <= limit <= capacity
private int mark = -1;
private int position = 0;
private int limit;
private int capacity; public final Buffer clear() {
position = 0;
limit = capacity;
mark = -1;
return this;
} public final Buffer flip() {
limit = position;
position = 0;
mark = -1;
return this;
}

 首先我们要明确一点,所谓的缓冲器仅仅是一个"多功能"的数组。可能在这个Buffer类中没有体现,但是如果我们打开ByteBuffer的源码会有byte[]的数组,打开CharBuffer的源码会有char[]的数组。因为Buffer是所有缓冲器的父类,所以他它不能预计会有多少种缓冲器,所以索性让"儿子"们自己实现去吧。

  既然知道了缓冲器是一个"多功能的数组",那么我们用画图的形式来分析一下上面Buffer的源码。

假设我们定义了一个8个单位大的缓冲区,如上图(其实Buffer也就是这么一个东西)。首先告诉大家那三个重要的关于缓冲区状态的的属性:

  capacity:缓冲区的容量;

  limit:缓冲区还有多少数据能够取出或者缓冲区还有多少容量用于存放数据;

  position:相当于一个游标(cursor),记录我们从哪里开始写数据,从哪里开始读数据。

刚还说到flip()和clear()是Buffer的两个重要的方法,因为它们两个方法决定了缓冲是否能正常的进行读写工作。

  当我们要想从缓冲区中写数据的时候必须先执行flip()方法,当我们要想从缓冲区中读数据时必须先执行clear()方法。

第一次向Buffer中写入数据时,执行一次flip()方法以后,Buffer的结构变成了这样:position指向了第一个可以存取数据的0号位,limit和capacity同时指向最高位。

假如第一次我们向Buffer中写入了3单位的数据,我们再次执行flip()方法则Buffer的结构会变成上图的所示。但是经过flip()的改造后position总是指向Buffer中第一个可用的位置。那么,未执行flip()方法以前position在哪里呢?很简单,指向最后一个数据的位置。

当我们想要从Buffer中读取数据时,执行clear()方法,Buffer的内部结构变成了上图所示,position指向了可读数据的首位,limit指向了原来position的位置。

  从上面的几幅图中我们看出:capacity代表了Buffer的容量是不变的,limit与position的差总是表示Buffer总可以读的数据,或者Buffer中可以写数据的容量。还有position总是小于等于limit,limit总是小于等于capacity。

  其实到这里我们已经发现,NIO并不像IO那么复杂,因为IO 中的Decorator模式和Adaptor模式确实让我们一时间摸不到头脑,但是熟悉了会感觉到IO的设计之精美。

  NIO中还有一个知识点就是无阻塞的Socket编程,这里就不说了,因为比较复杂,但是如果我们真正理解了Selector这个调度者的工作,那么无阻塞的实现机制我们差不多就掌握了,复杂也就是编码上面的事了。

我来说说java的NIO的更多相关文章

  1. JAVA bio nio aio

    [转自]http://qindongliang.iteye.com/blog/2018539 在高性能的IO体系设计中,有几个名词概念常常会使我们感到迷惑不解.具体如下: 序号 问题 1 什么是同步? ...

  2. java的nio之:java的nio系列教程之buffer的概念

    一:java的nio的buffer==>Java NIO中的Buffer用于和NIO通道Channel进行交互.==>数据是从通道channel读入缓冲区buffer,从缓冲区buffer ...

  3. java的nio之:java的nio系列教程之channel的概念

    一:java的nio的channel Java NIO的通道类似流,但又有些不同: ==>既可以从通道中读取数据,又可以写数据到通道.但流的读写通常是单向的. ==>通道可以异步地读写. ...

  4. java的nio之:java的nio系列教程之概述

    一:java的nio的核心组件?Java NIO 由以下几个核心部分组成: ==>Channels ==>Buffers ==>Selectors 虽然Java NIO 中除此之外还 ...

  5. java之NIO编程

    所谓行文如编程,随笔好比java文件,文章好比类,参考文献是import,那么目录就是方法定义. 本篇文章处在分析thrift的nonblocking server之前,因为后者要依赖该篇文章的知识. ...

  6. 输入和输出--java的NIO

    Java的NIO 实际开发中NIO使用到的并不多,我并不是说NIO使用情景不多,是说我自己接触的并不是很多,前面我在博客园和CSDN上转载了2篇别人写的文章,这里来大致总结下Java的NIO,大概了解 ...

  7. JAVA 探究NIO

    事情的开始 1.4版本开始,java提供了另一套IO系统,称为NIO,(New I/O的意思),NIO支持面向缓冲区的.基于通道的IO操作. 1.7版本的时候,java对NIO系统进行了极大的扩展,增 ...

  8. 理解Java的NIO

    同步与阻塞 同步和异步是针对应用程序和内核的交互而言的. 同步:执行一个操作之后,进程触发IO操作并等待(阻塞)或者轮询的去查看IO的操作(非阻塞)是否完成,等待结果,然后才继续执行后续的操作. 异步 ...

  9. Java通过NIO实现快速文件拷贝的代码

    将内容过程重要的内容片段做个记录,下面的内容段是关于Java通过NIO实现快速文件拷贝的内容. public static void fileCopy( File in, File out ) thr ...

  10. 一个小时就能理解Java的NIO必须掌握这三大要素!

    同步与阻塞 同步和异步是针对应用程序和内核的交互而言的. 同步:执行一个操作之后,进程触发IO操作并等待(阻塞)或者轮询的去查看IO的操作(非阻塞)是否完成,等待结果,然后才继续执行后续的操作. 异步 ...

随机推荐

  1. 一步一步学Silverlight 2系列(14):数据与通信之WCF

    一步一步学Silverlight 2系列(14):数据与通信之WCF   概述 Silverlight 2 Beta 1版本发布了,无论从Runtime还是Tools都给我们带来了很多的惊喜,如支持框 ...

  2. 《算法概论》第八章的一些课后题目 关于NP-Complete Problem

    8.3 STINGY SAT STINGY SAT is the following problem: given a set of clauses (each a disjunction of li ...

  3. JAVA GUI THREAD---***

    针对用户界面的多线程 GUI下面的多线程方式 1.与GUI类分离方式 分离方式,在创建线程类实例时需要代入GUI句柄,通过GUI句柄操作GUI,也就是说线程类和GUI类都要有对方的实例,以便相互操作. ...

  4. node mkdirSync 创建多级目录

    提供一个实用的一次性同步创建多级目录的方法,收藏一下. function makeDir(dirpath) { if (!fs.existsSync(dirpath)) { var pathtmp; ...

  5. Spring MVC访问页面直接显示源码

    转自:https://blog.csdn.net/u011781521/article/details/78751253

  6. Get与Post的小知识

    Get与Post的小知识 一.传递参数: Get把参数包含在URL中,而在Post通过request body传递参数.因为参数直接暴露在URL上,GET比POST更不安全,所以不能用来传递敏感信息. ...

  7. Event Handling Guide for iOS--(二)---Gesture Recognizers

    Gesture Recognizers 手势识别器 Gesture recognizers convert low-level event handling code into higher-leve ...

  8. bzoj 4557: [JLoi2016]侦察守卫【树形dp】

    设f[u][i]为u点向下覆盖至少i层并且处理完u的子树的最小代价,f[u][i]为u点向上覆盖至少i层并且处理完u的子树的最小代价 转移的话显然f[u][i]+=f[v][i-1],但是f[u][0 ...

  9. 第十篇 .NET高级技术之委托

    委托是一种可以指向方法的数据类型,可以声明委托类型变量. 声明委托的方式:delegate返回值类型   委托类型名(参数) 比如delegate void MyDel(int n) 注意这里的除了前 ...

  10. hbase-shell + hbase的java api

    本博文的主要内容有 .HBase的单机模式(1节点)安装 .HBase的单机模式(1节点)的启动 .HBase的伪分布模式(1节点)安装   .HBase的伪分布模式(1节点)的启动    .HBas ...