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. codeforces 454B. Little Pony and Sort by Shift 解题报告

    题目链接:http://codeforces.com/problemset/problem/454/B 题目意思:给出一个序列你 a1, a2, ..., an. 问每次操作只能通过将最后一个数拿出来 ...

  2. golang copy函数

    数组切片内容复制 转自:http://studygolang.com/articles/4560 用于将内容从一个数组切片复制到另一个数组切片.如果加入的两个数组切片不一样大,就会按其中较小的那个数组 ...

  3. apt-get update 问题 及gcc高亮

    一 gcc高亮 gcc 高亮有好几种方法. 参考 http://www.cokco.cn/thread-39909-1-1.html 这个教程: (1) git clone https://githu ...

  4. AutoIt: send 命令 VS ControlClick的使用

    2008年的时候第一次接触AutoIt,当时觉得局限性太多了,就不想学,觉得把Watir,Ruby搞好就行了. 最近一段时间比较闲,发现自己对GUI的自动化操完全是短板,就把AutoIt重新拾起来了. ...

  5. bzoj1003物流运输——DP

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1003 DP好题: 直接找一个时间段的最短路,并用它来预处理出每个时间段的最小花费: f[i] ...

  6. html 样式之style属性的使用

    转自:https://www.ggbiji.com/html-style.html html中的style属性是用来改变html元素的样式的,样式是 在html 4 引入的,它是改变 html元素样式 ...

  7. Table View Programming Guide for iOS---(四)---Navigating a Data Hierarchy with Table Views

    Navigating a Data Hierarchy with Table Views 导航数据表视图层次 A common use of table views—and one to which ...

  8. git操作实战指南

    1 背景 小白进入公司,进入日常多人开发,git的使用应该是新人要掌握的第一个技能.git是一个分布式数据存储库,分为远程存储和本地存储,本地存储的话,每一台计算机就相当于一个存储数据库,可以记录和存 ...

  9. 鉴于spfa基础上的差分约束算法

    怎么搞?        1. 如果要求最大值      想办法把每个不等式变为标准x-y<=k的形式,然后建立一条从y到x权值为k的边,变得时候注意x-y<k =>x-y<=k ...

  10. 一道简单的 Java 笔试题,但值得很多人反思!

    前言 面试别人,对我来说是一件新奇事,以前都是别人面试我.我清楚地知道,我在的地域与公司,难以吸引到中国的一流软件人才.所以,我特地调低了期望,很少问什么深入的技术问题,只问一些广泛的.基础的.我只要 ...