Channel是一个连接到数据源的通道。程序不能直接用Channel中的数据,必须让Channel与BtyeBuffer交互数据,才能使用Buffer中的数据。

我们用FileChannel作为引子,开始逐步的了解NIO中的重要一环——Channel

FileChannel

有了前面的知识积累,我可以更快速的学习。FileChannel中常用的操作无非那么几种,打开FileChannel、用BtyeBuffer从FileChannel中读数据、用BtyeBuffer向FileChannel中写数据,下面这段代码就展示了这些

/*
* 1.Channel是需要关闭的,所以这里用TWR方式确保Channel正确关闭
* 2.鼓励大家用这种方法打开通道FileChannel.open(Path path, OpenOption... options)
*/
try (FileChannel inChannel
= FileChannel.open(Paths.get("src/a.txt"),StandardOpenOption.READ);
FileChannel outChannel
= FileChannel.open(Paths.get("src/b.txt"),StandardOpenOption.WRITE);) {
ByteBuffer buf = ByteBuffer.allocate(48);
/*
* 1.channel.write()和read()方法是需要移动position和limit指针的
* 所以需要用buffer.flip()等方法,来保证读写正确
* 2.channel.read()方法是从通道读取到缓冲区中,读取的字节数量是n (n是buffer中当前剩余的容量),
* 但是读取的数量是取决于通道的当前状态。例如:要读到文件末尾,不够buffer的容量也就是 通道剩余<=n,
* 或者说ServerSocketChannel 当前只能读取准备好的,这很可能<n,所以说加循环,
* 另外read的方法返回当前读取的数量,一个int 可以根据他来设定while
* 如果返回-1,表示到了文件末尾
*/
int bytesRead = inChannel.read(buf);
while (bytesRead != -1) {
buf.flip();
/*
*注意fileChannel.write()是在while循环中调用的。
*因为无法保证write()方法一次能向FileChannel写入多少字节,
*因此需要重复调用write()方法,直到Buffer中已经没有尚未写入通道的字节。
*/
while (buf.hasRemaining()) {
outChannel.write(buf);
}
buf.clear();
bytesRead = inChannel.read(buf);
}
}

其实掌握了Buffer的知识后,学起FileChannel来挺容易的。而且再告诉你一点,就是如果只是将一个数据源通过FileChannel,转移到另一个数据源,还有一种更加简单的方法

try (FileChannel inChannel
= FileChannel.open(Paths.get("src/a.txt"),StandardOpenOption.READ);
FileChannel outChannel
= FileChannel.open(Paths.get("src/b.txt"),StandardOpenOption.WRITE);) {
//第二个参数表示,数据转移的起始位置,第三个参数表示转移的长度
//channel.size()表示通道的长度
outChannel.transferFrom(inChannel,0,inChannel.size());
//以下方式也可
inChannel.transferTo(0, inChannel.size(), outChannel);
}

这些以外,还有几个常用的方法,在这里要跟大家说一下

fileChannel.position() 返回FileChannel读写的当前位置

fileChannel.position(long newPosition) 设置FileChannel读写的当前位置

fileChannel.truncate(long size) 截取文件的前size个字节

fileChannel.force(boolean metaData) 将通道里尚未写入磁盘的数据强制写到磁盘上。出于性能方面的考虑,操作系统会将数据缓存在内存中,所以无法保证写入到FileChannel里的数据一定会即时写到磁盘上。要保证这一点,需要调用force()方法。其中的boolean类型的参数,指明是否同时将文件元数据(权限信息等)写到磁盘上。

下面理论上要介绍SocketChannel ServerSocketChannel等通道了,但是这些网络通道和fileChannel不太一样,因为fileChannel竟然是阻塞的(NIO,你在跟我开玩笑吧!),真的是阻塞的。而SocketChannel ServerSocketChannel等通道才是非阻塞的(fileChannel是充话费送的吧!),所以它们的真正能力要配合Selector才能显示出来,所以等到讲解Selector时,在一起讲。

那这样就结束了吗?当然不可能(这种看右边的滚动条就能发现的事实,就不要故弄玄虚了吧! 咦,我怎么在自己吐槽自己?)下面开始讲讲java7中引进的AsynchronousFileChannel,这回放心,它是异步的。

异步I/O (AIO)

其实利用java7之前的方式,也能做到,但是必须写大量的多线程代码,而且多路读取也十分麻烦。除非程序写的十分强大,否则,自己写的异步I/O的速度只能是 慢~~~~~~~~~

在java7的异步I/O中,主要有两种形式,将来式回调式。这是在java.util.concurrent中的并发工具,不会的话也没关系,在这里应该能大致的看懂。

将来式

这种方式是由主线程发起I/O操作并轮询等待结果。这里用了java.util.concurrent.Future接口,它的能力是不让当前线程阻塞。通过将I/O操作转移到另一线程上,并在完成时返回结果,来达到异步的目的。

try (AsynchronousFileChannel inChannel = AsynchronousFileChannel.open(
Paths.get("src/a.txt"), StandardOpenOption.READ);) {
ByteBuffer buffer = ByteBuffer.allocate(1024);
//read的第二个参数指定了channel的起始位置
Future<Integer> result = inChannel.read(buffer, 0);
//一直轮询I/O操作是否完成
while (!result.isDone()) {
// 做点别的
}
buffer.flip();
while (buffer.hasRemaining()) {
System.out.print((char) buffer.get());
}
}

回调式

这种方式是预先制定好I/O成功或失败时的应对策略,等待I/O操作完成后就自动执行该策略。所以必须得重写两个方法completionHandler.completed()和completionHandler.failed().

try (AsynchronousFileChannel inChannel = AsynchronousFileChannel.open(
Paths.get("src/a.txt"), StandardOpenOption.READ);) {
ByteBuffer buffer = ByteBuffer.allocate(1024);
/*
* asynchronousFileChannel.read(ByteBuffer dst,long position,
* A attachment, CompletionHandler<Integer,? super A> handler)
* 该函数是回调式中的核心函数
* 1.首先讲最后一个参数,它的第二个泛型类型和第三个参数类型一致为A
* 该接口有两个待实现的方法,completed(...)和failed(...) 分别代指完成时和失败时如何操作
* completed(Integer result, A attachment)的第一个参数是完成了多少个字节
* failed(Throwable exc, A attachment)的第一个参数是引起失败的异常类型
* 2.A 可以理解为在CompletionHandler的实现外部,要给实现内部什么信息
* 在下面的代码中,我传的A为buffer,以便实现的内部打印buffer信息,也可以传递String类型等
* 3.前两个参数分别为与通道交互的byteBuffer和起始位置
*/
inChannel.read(buffer, 0, buffer,
new CompletionHandler<Integer, ByteBuffer>() {
public void completed(Integer result,
ByteBuffer attachment) {
System.out.println(result);
attachment.flip();
while (attachment.hasRemaining()) {
System.out.print((char) attachment.get());
}
} public void failed(Throwable exception,
ByteBuffer attachment) {
System.out.println("failed"
+ exception.getMessage());
}
}); // 做点别的
}

纵观这两种异步I/O实现方式,我自己总感觉,将来式总是询问数据是否到位,有股非阻塞I/O的感觉。网络异步I/O也是运用将来式和回调式完成的,和文件I/O基本一致,就不再磨叽。

但java7的I/O新内容绝不止这些,还有对网络多播的支持,还有通道组等等。想学完?路还有很远、很远呢。

讲的就是这么多,如有问题联系我

Java NIO教程 Channel的更多相关文章

  1. Java NIO教程 目录

    "Java NIO系列教程" 是笔者hans为NIO的初学者编写的一份入门教程,想仔细学习的同学可以按照顺序去阅读.由于我学的也不是特别的精,所以错误.疏漏在所难免,希望同学们指正 ...

  2. Java NIO 之 Channel(通道)

    历史回顾: Java NIO 概览 Java NIO 之 Buffer(缓冲区) 其他高赞文章: 面试中关于Redis的问题看这篇就够了 一文轻松搞懂redis集群原理及搭建与使用 一 Channel ...

  3. 海纳百川而来的一篇相当全面的Java NIO教程

    目录 零.NIO包 一.Java NIO Channel通道 Channel的实现(Channel Implementations) Channel的基础示例(Basic Channel Exampl ...

  4. Java NIO 教程

    Java NIO(New IO)是从Java 1.4版本开始引入的一个新的IO API,可以替代标准的Java IO API.本系列教程将有助于你学习和理解Java NIO. Java NIO提供了与 ...

  5. Java NIO教程 前言

    阅读本文前,建议你先了解 旧I/O NIO 是 New I/O 的缩写,要了解它真正的内涵,需要掌握的知识还是比较多的.我努力在这几篇笔记里,勾勒出整个io的面貌.为大家的深入学习铺路. I/O简史 ...

  6. Java NIO教程 MappedByteBuffer

    之前跟大家说过,要讲MappedByteBuffer,现在我来履行承诺了. 首先从大体上讲一下MappedByteBuffer究竟是什么.从继承结构上来讲,MappedByteBuffer继承自Byt ...

  7. [翻译] java NIO 教程---介绍

    原文地址:http://tutorials.jenkov.com/java-nio/index.html Java NIO(new IO)是从java1.4之后的对IO API的另一种选择,即对标准j ...

  8. [转载] Java NIO教程

    转载自并发编程网 – ifeve.com http://ifeve.com/java-nio-all/ 关于通道(Channels).缓冲区(Buffers).选择器(Selectors)的故事. 从 ...

  9. Java NIO -- 通道 Channel

    通道(Channel):由 java.nio.channels 包定义的.Channel 表示 IO 源与目标打开的连接.Channel 类似于传统的“流”.只不过 Channel本身不能直接访问数据 ...

随机推荐

  1. Lucene/Solr搜索引擎开发笔记 - 第2章 Solr安装与部署(Tomcat篇)

    一.安装环境 图1-1 Tomcat和Solr的版本 我本机目前使用的Java版本为JDK 1.8,因为Solr 4.9要求Java版本为1.7+,请注意. 二.Solr部署到Tomcat流程 图1- ...

  2. visual.studio.15.preview5 编译器

    前段时间微软更新了新版开发工具visual studio 15 preview5,安装后连文件结构目录都变了,想提取编译器还找不到. 不是原来的VC\BIN目录,已迁移到IDE\MSVC\14.10. ...

  3. oracle 创建表空间

    --创建数据表空间 create tablespace hcm logging datafile 'G:\oracle\product\10.2.0\oradata\orcl\mydata.dbf' ...

  4. RPC远程过程调用协议

    最近学习Hadoop.Hbase.Spark及Storm原理,经常会出现RPC这样的传输术语,为了更好地理解,将知识点详细的整理下吧~ RPC-----它是一种通过网络从远程计算机程序上请求服务,而不 ...

  5. linux gcc头文件搜索路径

    #include <>: 直接到系统指定的某些目录中去找某些头文件.#include "": 先到源文件所在文件夹去找,然后再到系统指定的某些目录中去找某些头文件 1. ...

  6. vue.js 2.0开发

    创建一个工程文件: css中引用的是bootstrap的css,js中就是vue,index页面: <!DOCTYPE html> <html> <head> &l ...

  7. view--4种Android获取View宽高的方式

    有时我们会有基于这样的需求,当Activity创建时,需要获取某个View的宽高,然后进行相应的操作,但是我们在onCreate,onStart中获取View的大小,获取到的值都是0,只是由于View ...

  8. [ActionScript 3.0] AS3.0 Socket通信实例

    以下类是充当Socket服务器的例子 package { import flash.display.Sprite; import flash.events.Event; import flash.ev ...

  9. U盘安装ubuntu server 12.04的问题检测不到CDROM的解决

    U盘安装ubuntu server 12.04的问题检测不到CDROM的解决 ========================== 我是u盘安装ubuntu 14 64Bit 也是出现同样的问题 用u ...

  10. 41. Unique Binary Search Trees && Unique Binary Search Trees II

    Unique Binary Search Trees Given n, how many structurally unique BST's (binary search trees) that st ...