一、通道是什么

通道式(Channel)是java.nio的第二个主要创新。通道既不是一个扩展也不是一项增强,而是全新的、极好的Java I/O示例,提供与I/O服务的直接连接。Channel用于在字节缓冲区和位于通道另一侧的实体(通常是一个文件或套接字)之间有效地传输数据

通常情况下,通道与操作系统的文件描述符(FileDescriptor)和文件句柄(FileHandler)有着一对一的关系。虽然通道比文件描述符更广义,但开发者经常使用到的多数通道都是连接到开放的文件描述符的。Channel类提供维持平台独立性所需的抽象过程,不然仍然会模拟现代操作系统本身的I/O性能。

通道是一种途径,借助该途径,可以用最小的总开销来访问操作系统本身的I/O服务。缓冲区则是通道内部用来发送和接收数据的端点,如下图:

二、通道基础

首先,看一下基本的Channel接口,下面是Channel接口的完整源码:

public interface Channel extends Closeable {

    /**
* Tells whether or not this channel is open. </p>
*
* @return <tt>true</tt> if, and only if, this channel is open
*/
public boolean isOpen(); /**
* Closes this channel.
*
* <p> After a channel is closed, any further attempt to invoke I/O
* operations upon it will cause a {@link ClosedChannelException} to be
* thrown.
*
* <p> If this channel is already closed then invoking this method has no
* effect.
*
* <p> This method may be invoked at any time. If some other thread has
* already invoked it, however, then another invocation will block until
* the first invocation is complete, after which it will return without
* effect. </p>
*
* @throws IOException If an I/O error occurs
*/
public void close() throws IOException; }
    和缓冲区不同,通道API主要由接口指定。不同的操作系统上通道实现会有根本性的差异,所以通道API仅仅描述了可以做什么,因此很自然地,通道实现经常使用操作系统的本地代码,通道接口允许开发者以一种受控且可移植的方式来访问底层的I/O服务。

可以从底层的Channel接口看到,对所有通道来说只有两种共同的操作:检查一个通道是否打开isOpen()和关闭一个打开的通道close(),其余所有的东西都是那些实现Channel接口以及它的子接口的类。

从Channel接口引申出的其他接口都是面向字节的子接口:

包括WritableByteChannel和ReadableByteChannel。这也正好支持了我们之前的所学:通道只能在字节缓冲区上操作。层次接口表明其他数据类型的通道也可以从Channel接口引申而来。这是一种很好的镭射机,不过非字节实现是不可能的,因为操作系统都是以字节的形式实现底层I/O接口的。

三、认识通道

看一下基本的接口:

public interface ReadableByteChannel extends Channel {

    public int read(ByteBuffer dst) throws IOException;

}
public interface WritableByteChannel
extends Channel
{ public int write(ByteBuffer src) throws IOException; }
public interface ByteChannel
extends ReadableByteChannel, WritableByteChannel
{ }

通道可以是单向的也可以是双向的。一个Channel类可能实现定义read()方法的ReadableByteChannel接口,而另一个Channel类也许实现WritableByteChannel接口以提供write()方法。实现这两种接口其中之一的类都是单向的,只能在一个方向上传输数据。如果一个类同时实现这两个接口,那么它是双向的,可以双向传输数据,就像上面的ByteChannel。

通道可以以阻塞(blocking)或非阻塞(nonblocking)模式运行,非阻塞模式的通道永远不会让调用的线程休眠,请求的操作要么立即完成,要么返回一个结果表明未进行任何操作。只有面向流的(stream-oriented)的通道,如sockets和pipes才能使用非阻塞模式

比方说非阻塞的通道SocketChannel:

public abstract class SocketChannel
extends AbstractSelectableChannel
implements ByteChannel, ScatteringByteChannel, GatheringByteChannel
{
...
}
public abstract class AbstractSelectableChannel
extends SelectableChannel
{
...
}

可以看出,socket通道类从SelectableChannel类引申而来,从SelectableChannel引申而来的类可以和支持有条件的选择的选择器(Selectors)一起使用。将非阻塞I/O和选择器组合起来可以使开发者的程序利用多路复用I/O。

四、认识文件通道

通道是访问I/O服务的导管,I/O可以分为广义的两大类:File I/O和Stream I/O。那么相应的,通道也有两种类型,它们是文件(File)通道和套接字(Socket)通道。文件通道指的是FileChannel,套接字通道则有三个,分别是SocketChannel、ServerSocketChannel和DatagramChannel。

通道可以以多种方式创建。Socket通道可以有直接创建Socket通道的工厂方法,但是一个FileChannel对象却只能通过在一个打开的RandomAccessFile、FileInputStream或FileOutputStream对象上调用getChannel()方法来获取,开发者不能直接创建一个FileChannel

文件I/O是我们最常使用的I/O,因此这部分先认识一下文件通道,下一部分再以代码形式演示如何使用文件通道高。用UML图表示一下文件通道的类层次关系:

    文件通道总是阻塞式的,因此不能被置于非阻塞模式下

前面提到过了,FileChannel对象不能直接创建,一个FileChannel实例只能通过在一个打开的File对象(RandomAccessFile、FileInputStream或FileOutputStream)上调用getChannel()方法获取,调用getChannel()方法会返回一个连接到相同文件的FileChannel对象且该FileChannel对象具有与file对象相同的访问权限,然后就可以使用通道对象来利用强大的FileChannel API了。

    FileChannel对象是线程安全的,多个进程可以在同一个实例上并发调用方法而不会引起任何问题,不过并非所有的操作都是多线程的。影响通道位置或者影响文件的操作都是单线程的,如果有一个线程已经在执行会影响通道位置或文件大小的操作,那么其他尝试进行此类操作之一的线程必须等待,并发行为也会受到底层操作系统或文件系统的影响。

五、使用文件通道读数据

讲了这么多理论,下面来看一下如何使用文件通道,首先是从文件中读出数据:

public static void main(String[] args) throws Exception
{
File file = new File("D:/files/readchannel.txt");
FileInputStream fis = new FileInputStream(file);
FileChannel fc = fis.getChannel();
ByteBuffer bb = ByteBuffer.allocate(35);
fc.read(bb);
bb.flip();
while (bb.hasRemaining())
{
System.out.print((char)bb.get());
}
bb.clear();
fc.close();
}

这是最简单的操作,前面讲过文件通道必须通过一个打开的RandomAccessFile、FileInputStream、FileOutputStream获取到,因此这里使用FileInputStream来获取FileChannel。接着只要使用read方法将内容读取到缓冲区内即可,缓冲区内有了数据,就可以使用前文对于缓冲区的操作读取数据了。文件里面的数据是:

控制台上打印出来的数据是:

channel1!
channel2!
channel3!

没有问题。

六、使用文件通道写数据

上面看了使用文件通道读数据,接着看一下使用文件通道写数据,差不多:

public static void main(String[] args) throws Exception
{
File file = new File("D:/files/writechannel.txt");
RandomAccessFile raf = new RandomAccessFile(file, "rw");
FileChannel fc = raf.getChannel();
ByteBuffer bb = ByteBuffer.allocate(10);
String str = "abcdefghij";
bb.put(str.getBytes());
bb.flip();
fc.write(bb);
bb.clear();
fc.close();
}

这里使用了RandomAccessFile去获取FileChannel,然后操作其实差不多,write方法写ByteBuffer中的内容至文件中,注意写之前还是要先把ByteBuffer给flip一下。

可能有人觉得这种连续put的方法非常不方便,但是没有办法,之前已经提到过了:通道只能使用ByteBuffer

Java NIO5:通道和文件通道的更多相关文章

  1. Java NIO3:通道和文件通道

    通道是什么 通道式(Channel)是java.nio的第二个主要创新.通道既不是一个扩展也不是一项增强,而是全新的.极好的Java I/O示例,提供与I/O服务的直接连接.Channel用于在字节缓 ...

  2. JAVA NIO之文件通道

    1.简介 通道是 Java NIO 的核心内容之一,在使用上,通道需和缓存类(ByteBuffer)配合完成读写等操作.与传统的流式 IO 中数据单向流动不同,通道中的数据可以双向流动.通道既可以读, ...

  3. Java IO和Java NIO 和通道 在文件拷贝上的性能差异分析

    1.  在JAVA传统的IO系统中,读取磁盘文件数据的过程如下: 以FileInputStream类为例,该类有一个read(byte b[])方法,byte b[]是我们要存储读取到用户空间的缓冲区 ...

  4. Java开发笔记(九十四)文件通道的性能优势

    前面介绍了字节缓存的一堆概念,可能有的朋友还来不及消化,虽然文件通道的用法比起传统I/O有所简化,可是平白多了个操控繁琐的字节缓存,分明比较传统I/O更加复杂了.尽管字节缓存享有缓存方面的性能优势,但 ...

  5. Java开发笔记(九十二)文件通道的基本用法

    前面介绍的各色流式IO在功能方面着实强大,处理文件的时候该具备的操作应有尽有,可流式IO在性能方面不尽如人意,它的设计原理使得实际运行效率偏低,为此从Java4开始增加了NIO技术,通过全新的架构体系 ...

  6. Java NIO 学习笔记(六)----异步文件通道 AsynchronousFileChannel

    目录: Java NIO 学习笔记(一)----概述,Channel/Buffer Java NIO 学习笔记(二)----聚集和分散,通道到通道 Java NIO 学习笔记(三)----Select ...

  7. Java NIO 学习笔记(四)----文件通道和网络通道

    目录: Java NIO 学习笔记(一)----概述,Channel/Buffer Java NIO 学习笔记(二)----聚集和分散,通道到通道 Java NIO 学习笔记(三)----Select ...

  8. java输入输出 -- java NIO之文件通道

    一.简介 通道是 Java NIO 的核心内容之一,在使用上,通道需和缓存类(ByteBuffer)配合完成读写等操作.与传统的流式 IO 中数据单向流动不同,通道中的数据可以双向流动.通道既可以读, ...

  9. Java NIO学习笔记五 FileChannel(文件通道)

    Java NIO FileChannel Java NIO FileChannel是连接文件的通道.使用FileChannel,您可以从文件中读取数据和将数据写入文件.Java NIO FileCha ...

随机推荐

  1. mybatis 中 使用 allowMultiQueries=true

    单条的数据进行修改或者插入的时候没问题,但是进行批量操作的时候就会出现错误,是因为没有开启支持批量操作的功能. mybatis支持批量操作 开启批量执行sql的开关,在拼装mysql链接的url时,为 ...

  2. 如何用ABP框架快速完成项目(14) - 结尾? 当然不是, 这只是开始!

    此文当前版本号: 3 最近更新时间: 2018-12-9 04:52   本课程是方向性课程, 目的是避免南辕北辙. 方向盘一旦打正确, 还得需要以下文章去写好具体程序: 前面每篇文章里面的链接, 比 ...

  3. <1>Python生成高质量Html文件:Pyh模块+Bootstrap框架

    一,介绍 QQ交流群:585499566 本文的目的是怎么使用Pyh+Bootstrap快速生成简约,大方,清新的Html页面,涉及到的技能:Python小白技能,Pyh会阅读中文文档,Html基础, ...

  4. leetcode-217存在重复元素

    leetcode-217存在重复元素 题意 给定一个整数数组,判断是否存在重复元素. 如果任何值在数组中出现至少两次,函数返回 true.如果数组中每个元素都不相同,则返回 false. 示例 1: ...

  5. Wu反走样算法绘制圆(C++/MFC实现)

    Wu反走样圆 原理:参考Bresenham算法,在主位移过程中计算出离理想圆最近的两个点,赋予不同的亮度值,绘制像素点即可! MFC 中CXXXView类中添加函数: //Wu算法画反走样圆 void ...

  6. git 入门教程之1分钟快速了解 git

    git 入门教程 git 是分布式版本控制系统,是文本文档管理的利器,是帮助你管理文件动态的好帮手. 如果你曾经手动管理过文档,一定有这样的经历,比如你正在编辑文档,想删除某段落,又担心不久后可能会恢 ...

  7. (其他)用sublime text3编写的html网页用浏览器打开出现中文乱码的原理及解决方法(转)

    最近发现Hbuler比较难用,换成sublime text3了,用了以前没用过的软件,就要学习他的操作,刚上手就出了点问题. 解决方法就是sublime text3以utf8 with bom保存. ...

  8. 使用Visual Studio Team Services进行压力和性能测试(一)——创建基础的URL压力测试

    使用Visual Studio Team Services进行压力和性能测试(一)--创建基础的URL压力测试 概述 压力测试使应用程序更加健壮,并审核在用户负载下的行为,这样我们可以在当前的基础设施 ...

  9. 惰性求值——lodash源码解读

    前言 lodash受欢迎的一个原因,是其优异的计算性能.而其性能能有这么突出的表现,很大部分就来源于其使用的算法--惰性求值. 本文将讲述lodash源码中,惰性求值的原理和实现. 一.惰性求值的原理 ...

  10. Scala高阶函数实践

    高阶函数主要有两种:一种是将一个函数当做另外一个函数的参数(即函数参数):另外一种是返回值是函数的函数.package sparkCore/** * Created by zhen on 2018/3 ...