简介

小师妹,你还记得我们使用IO和NIO的初心吗?

小师妹:F师兄,使用IO和NIO不就是为了让生活更美好,世界充满爱吗?让我等程序员可以优雅的将数据从一个地方搬运到另外一个地方。利其器,善其事,才有更多的时间去享受生活呀。

善,如果将数据比做人,IO,NIO的目的就是把人运到美国。

小师妹:F师兄,为什么要运到美国呀,美国现在新冠太严重了,还是待在中国吧。中国是世界上最安全的国家!

好吧,为了保险起见,我们要把人运到上海。人就是数据,怎么运过去呢?可以坐飞机,坐汽车,坐火车,这些什么飞机,汽车,火车就可以看做是一个一个的Buffer。

最后飞机的航线,汽车的公路和火车的轨道就可以看做是一个个的channel。

更多精彩内容且看:

简单点讲,channel就是负责运送Buffer的通道。

IO按源头来分,可以分为两种,从文件来的File IO,从Stream来的Stream IO。不管哪种IO,都可以通过channel来运送数据。

Channel的分类

虽然数据的来源只有两种,但是JDK中Channel的分类可不少,如下图所示:

先来看看最基本的,也是最顶层的接口Channel:

public interface Channel extends Closeable {
public boolean isOpen();
public void close() throws IOException; }

最顶层的Channel很简单,继承了Closeable接口,需要实现两个方法isOpen和close。

一个用来判断channel是否打开,一个用来关闭channel。

小师妹:F师兄,顶层的Channel怎么这么简单,完全不符合Channel很复杂的人设啊。

别急,JDK这么做其实也是有道理的,因为是顶层的接口,必须要更加抽象更加通用,结果,一通用就发现还真的就只有这么两个方法是通用的。

所以为了应对这个问题,Channel中定义了很多种不同的类型。

最最底层的Channel有5大类型,分别是:

FileChannel

这5大channel中,和文件File有关的就是这个FileChannel了。

FileChannel可以从RandomAccessFile, FileInputStream或者FileOutputStream中通过调用getChannel()来得到。

也可以直接调用FileChannel中的open方法传入Path创建。

public abstract class FileChannel
extends AbstractInterruptibleChannel
implements SeekableByteChannel, GatheringByteChannel, ScatteringByteChannel

我们看下FileChannel继承或者实现的接口和类。

AbstractInterruptibleChannel实现了InterruptibleChannel接口,interrupt大家都知道吧,用来中断线程执行的利器。来看一下下面一段非常玄妙的代码:

protected final void begin() {
if (interruptor == null) {
interruptor = new Interruptible() {
public void interrupt(Thread target) {
synchronized (closeLock) {
if (closed)
return;
closed = true;
interrupted = target;
try {
AbstractInterruptibleChannel.this.implCloseChannel();
} catch (IOException x) { }
}
}};
}
blockedOn(interruptor);
Thread me = Thread.currentThread();
if (me.isInterrupted())
interruptor.interrupt(me);
}

上面这段代码就是AbstractInterruptibleChannel的核心所在。

首先定义了一个Interruptible的实例,这个实例中有一个interrupt方法,用来关闭Channel。

然后获得当前线程的实例,判断当前线程是否Interrupted,如果是的话,就调用Interruptible的interrupt方法将当前channel关闭。

SeekableByteChannel用来连接Entry或者File。它有一个独特的属性叫做position,表示当前读取的位置。可以被修改。

GatheringByteChannel和ScatteringByteChannel表示可以一次读写一个Buffer序列结合(Buffer Array):

public long write(ByteBuffer[] srcs, int offset, int length)
throws IOException;
public long read(ByteBuffer[] dsts, int offset, int length)
throws IOException;

Selector和Channel

在讲其他几个Channel之前,我们看一个和下面几个channel相关的Selector:

这里要介绍一个新的Channel类型叫做SelectableChannel,之前的FileChannel的连接是一对一的,也就是说一个channel要对应一个处理的线程。而SelectableChannel则是一对多的,也就是说一个处理线程可以通过Selector来对应处理多个channel。

SelectableChannel通过注册不同的SelectionKey,实现对多个Channel的监听。后面我们会具体的讲解Selector的使用,敬请期待。

DatagramChannel

DatagramChannel是用来处理UDP的Channel。它自带了Open方法来创建实例。

来看看DatagramChannel的定义:

public abstract class DatagramChannel
extends AbstractSelectableChannel
implements ByteChannel, ScatteringByteChannel, GatheringByteChannel, MulticastChannel

ByteChannel表示它同时是ReadableByteChannel也是WritableByteChannel,可以同时写入和读取。

MulticastChannel代表的是一种多播协议。正好和UDP对应。

SocketChannel

SocketChannel是用来处理TCP的channel。它也是通过Open方法来创建的。

public abstract class SocketChannel
extends AbstractSelectableChannel
implements ByteChannel, ScatteringByteChannel, GatheringByteChannel, NetworkChannel

SocketChannel跟DatagramChannel的唯一不同之处就是实现的是NetworkChannel借口。

NetworkChannel提供了一些network socket的操作,比如绑定地址等。

ServerSocketChannel

ServerSocketChannel也是一个NetworkChannel,它主要用在服务器端的监听。

public abstract class ServerSocketChannel
extends AbstractSelectableChannel
implements NetworkChannel

AsynchronousSocketChannel

最后AsynchronousSocketChannel是一种异步的Channel:

public abstract class AsynchronousSocketChannel
implements AsynchronousByteChannel, NetworkChannel

为什么是异步呢?我们看一个方法:

public abstract Future<Integer> read(ByteBuffer dst);

可以看到返回值是一个Future,所以read方法可以立刻返回,只在我们需要的时候从Future中取值即可。

使用Channel

小师妹:F师兄,讲了这么多种类的Channel,看得我眼花缭乱,能不能讲一个Channel的具体例子呢?

好的小师妹,我们现在讲一个使用Channel进行文件拷贝的例子,虽然Channel提供了transferTo的方法可以非常简单的进行拷贝,但是为了能够看清楚Channel的通用使用,我们选择一个更加常规的例子:

public void useChannelCopy() throws IOException {
FileInputStream input = new FileInputStream ("src/main/resources/www.flydean.com");
FileOutputStream output = new FileOutputStream ("src/main/resources/www.flydean.com.txt");
try(ReadableByteChannel source = input.getChannel(); WritableByteChannel dest = output.getChannel()){
ByteBuffer buffer = ByteBuffer.allocateDirect(1024);
while (source.read(buffer) != -1)
{
// flip buffer,准备写入
buffer.flip();
// 查看是否有更多的内容
while (buffer.hasRemaining())
{
dest.write(buffer);
}
// clear buffer,供下一次使用
buffer.clear();
}
}
}

上面的例子中我们从InputStream中读取Buffer,然后写入到FileOutputStream。

总结

今天讲解了Channel的具体分类,和一个简单的例子,后面我们会再体验一下Channel的其他例子,敬请期待。

本文的例子https://github.com/ddean2009/learn-java-io-nio

本文作者:flydean程序那些事

本文链接:http://www.flydean.com/java-io-nio-channel/

本文来源:flydean的博客

欢迎关注我的公众号:程序那些事,更多精彩等着您!

小师妹学JavaIO之:NIO中Channel的妙用的更多相关文章

  1. 小师妹学JavaIO之:NIO中那些奇怪的Buffer

    目录 简介 Buffer的分类 Big Endian 和 Little Endian aligned内存对齐 总结 简介 妖魔鬼怪快快显形,今天F师兄帮助小师妹来斩妖除魔啦,什么BufferB,Buf ...

  2. 小师妹学JavaIO之:文件系统和WatchService

    目录 简介 监控的痛点 WatchService和文件系统 WatchSerice的使用和实现本质 总结 简介 小师妹这次遇到了监控文件变化的问题,F师兄给小师妹介绍了JDK7 nio中引入的Watc ...

  3. 小师妹学JavaIO之:用Selector来发好人卡

    目录 简介 Selector介绍 创建Selector 注册Selector到Channel中 SelectionKey selector 和 SelectionKey 总的例子 总结 简介 NIO有 ...

  4. 小师妹学JavaIO之:MappedByteBuffer多大的文件我都装得下

    目录 简介 虚拟地址空间 详解MappedByteBuffer MapMode MappedByteBuffer的最大值 MappedByteBuffer的使用 MappedByteBuffer要注意 ...

  5. 小师妹学JavaIO之:Buffer和Buff

    目录 简介 Buffer是什么 Buffer进阶 创建Buffer Direct VS non-Direct Buffer的日常操作 向Buffer写数据 从Buffer读数据 rewind Buff ...

  6. 小师妹学JVM之:JIT中的PrintAssembly

    目录 简介 使用PrintAssembly 输出过滤 总结 简介 想不想了解JVM最最底层的运行机制?想不想从本质上理解java代码的执行过程?想不想对你的代码进行进一步的优化和性能提升? 如果你的回 ...

  7. 小师妹学JavaIO之:目录还是文件

    目录 简介 linux中的文件和目录 目录的基本操作 目录的进阶操作 目录的腰疼操作 总结 简介 目录和文件傻傻分不清楚,目录和文件的本质到底是什么?在java中怎么操纵目录,怎么遍历目录.本文F师兄 ...

  8. 小师妹学JavaIO之:文件File和路径Path

    简介 文件和路径有什么关系?文件和路径又隐藏了什么秘密?在文件系统的管理下,创建路径的方式又有哪些?今天F师兄带小师妹再给大家来一场精彩的表演. 文件和路径 小师妹:F师兄我有一个问题,java中的文 ...

  9. 小师妹学JVM之:JDK14中JVM的性能优化

    目录 简介 String压缩 分层编译(Tiered Compilation) Code Cache分层 新的JIT编译器Graal 前置编译 压缩对象指针 Zero-Based 压缩指针 Escap ...

随机推荐

  1. ASHRAE KAGGLE大能源预测(前三名方案总结+相关知识点讲解+python实现)

    @ 目录 1 概述 2 处理思想学习 2.1 移除异常值 2.2 缺失值 2.3 目标函数 2.4 特征工程 2.4.1 Savitzky-Golay filter 2.4.2 Bayesian ta ...

  2. DPDK Hash Library原理(学习笔记)

    0 前言 本文主要翻译至DPDK的官方编程指南,在谷歌翻译的基础上根据自己的理解做了一些修改.网上搜索的很多中文翻译大多是翻译后直接黏贴上来,有时候连语句都读不通.希望本文能够对你有所帮助. 1 介绍 ...

  3. mysql日志体系大盘点

    MySql日志文件主要包含:错误日志.慢查询日志.事务日志.二进制日志等 Mysql的日志配置可以通过命令 show global variables like '%log%'; 执行的结果如下 &q ...

  4. 为什么我不建议你通过 Python 去找工作?

    二哥,你好,我是一名大专生,学校把 Python 做为主语言教给我们,但是我也去了解过,其实 Python 门槛挺高的,所以我在自学 Java,但是我现在并不清楚到底要不要全心的去学 Java,学校里 ...

  5. dij+DP

    众所周知,蒜蒜是一名热爱工作的好员工,他觉得时间就是金钱,做事情总是争分夺秒. 这天晚上,蒜蒜一个人去吃晚饭.不巧的是,吃完饭以后就开始下雨了,蒜蒜并没有带雨伞出来.但是蒜蒜热爱工作,工作使他快乐,他 ...

  6. JavaScript实现队列结构

    参考资料 一.什么是队列结构? 1.1.简介 队列(Queue),类似于栈结构,但又和栈结构不同 是一种运算受限的线性表,受限之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rea ...

  7. 解释一下,@SpringBootApplication

    解释一下,@SpringBootApplication其实就是以下三个注解的总和 @Configuration: 用于定义一个配置类 @EnableAutoConfiguration :Spring ...

  8. CDN是啥?

    CDN 介绍 CDN ( Content Delivery Network ),也即内容分发网络.通过将网站内容(如图片.JavaScript .CSS.网页等)分发至全网加速节点,配合精准智能调度系 ...

  9. js数组对象的一些常用方法

    pop:删除数组最后一个元素 语法: array.pop(); 如 var array = ['1','2','3']; array.pop(); 返回结果:[‘1’,‘2’]此方法会改变数组的长度 ...

  10. Docker安装常见的应用与将本地镜像推送到阿里云

    一.Docker安装常用的应用 1,docker安装mysql #拉取镜像mysql5.7 docker pull mysql:5.7 #启动容器(绑定对应的配置文件和日志,默认密码为123456) ...