打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
JavaNIO中的Channel概述
前面一些Java的文章已经简要对Java NIO做了描述,也详细地介绍了NIO中最基本的Buffer。这篇文章将继续介绍NIO的第二个主要角色 —— Channel。Channel是对数据的源头和数据目标点流经途径的抽象,在这个意义上和InputStream和OutputStream类似。Channel可以译为“通道、管道”,而传输中的数据仿佛就像是在其中流淌的水。前面也提到了Buffer,Buffer和Channel相互配合使用,才是Java的NIO。
0. Channel简述
“A nexus for I/O operations.”这是Java API中对Channel的描述,说的是Channel是IO操作的连接点。
开门见山,既然Channel是Java NIO中这么重要的一个角色,那么我们来看一下Java API中给出的类结构图。
Channel类层次结构(摘自《Java NIO》)
从上至下,前三层是描述和约定Channel功能的interface(除了java.nio.channels.spi. AbstractInterruptibleChannel),而在下面几层是和这些结构对应的Channel类。
图中也给出了各个interface和class的方法。Channel接口本身只是总体上提供了两个“不痛不痒”的方法。而紧接着的下面三个接口,各为自己的特征给出了一个方法约定。第三层的接口则是更进一步更具体的扩展。
再往下看,就是实现这些接口的类,从图中左右两侧可以看到,实际上是分为了两大类。一类是SelectableChannel,而另一类是FileChannel。而SelectableChannel实际上是网络通信相关的SocketChannel等的实现基础。
需要注意的是,图中下层的这些class实际上也都是abstract的,即抽象类。我们上面也说过,Channel是数据源和数据归宿之间通道的抽象。而实际的IO必然是和底层操作系统相关的,是需要依赖平台的具体实现,这些不会再高度抽象的跨平台Java API中体现,只是在JDK实现中有所不同。
除图中提到的以外,还有提供静态方法的工具类Channels。另外,后续的JDK1.7中页增加了新的内容。
1. Channel的使用和分类
之前在介绍ByteBuffer的时候就已经提到过了,Channel是同系统底层数据的交互,为了保证效率,统一使用字节为单位。从上面的类层次结构图中也可以看得出来,Channel的两个子接口分别是ReadableByteChannel和WritableByteChannel,而ByteChannel则对这两个接口做了合并归纳。而且SocketChannel和FileChannel都实现了这些接口。
需要注意的是,随着Java API的发展变化,接口的继承关系也和上图绘制的有所不同。如在JavaSE7中,增加了SeekableByteChannel接口等。尽管如此,大体上还是维持了原来的设计结构。
关于Channel对象的创建,有两类情况。对于Socket相关的Channel,只要调用特定类的open()方法即可,之后进行bind或者connect操作。而文件相关的FileChannel则不然,需要通过FileInputStream、FileOutputStream或者RandomAccessFile的getChannel()获取Channel对象。
1
2
3
4
5
6
7
8
9
10
SocketChannel sc = SocketChannel.open( );
sc.connect (new InetSocketAddress ("somehost", someport));
ServerSocketChannel ssc = ServerSocketChannel.open( );
ssc.socket( ).bind (new InetSocketAddress (somelocalport));
DatagramChannel dc = DatagramChannel.open( );
RandomAccessFile raf = new RandomAccessFile ("somefile", "r");
FileChannel fc = raf.getChannel( );
从这个地方,我们就发现,其实Channel根据IO服务的情况主要分为两大类,按照《Java NIO》的描述,两类IO分别是:file I/O 和 stream I/O。前者是针对文件读写操作的,而后者多是网络通信相关的和Socket相关的。Channel分类也基本如此,和前者对应的FileChannel,以及与后者对应的SocketChannel等类对象。
两者的区别不仅仅在Channel对象的构建和初始化上,更重要的还是和Selecotor的结合使用相关。也包括是否支持阻塞模式等。
接下来就是通过Channel进行实际的IO操作,即读和写。其实,这两项分别都在ReadableByteChannel和WritableByteChannel中声明了的。ByteChannel的子类都有
1
2
public int read (ByteBuffer dst) throws IOException;
public int write (ByteBuffer src) throws IOException;
这两个方法。
需要注意的是,尽管你所拿到的Channel对象都是有read()和write()方法的,但并不代表他们一定是可读或者可写的。是否可以读写需要了解这个Channel的来头,这是比较讨厌的一点。
读写之后,通常就要进行关闭操作。关闭的概念其实很简单,当一个Channel被关闭后,就不再可用。相关的close()和isOpen()方法可以进行关闭和检查Channel是否是打开状态。执行close()可能会对线程造成阻塞,当一个Channel关闭后,后续的close()操作直接返回。
当Channel对象的close和中断结合起来,就有些要提到的了。一个阻塞在Channel对象上的线程如果收到了中断操作,则Channel对象关闭,线程收到ClosedByInterruptException异常。而把顺序稍微调整一下,一个已经被set中断信号的线程访问Channel对象时,Channel对象也会被关闭,线程得到ClosedByInterruptException异常。其它阻塞在这个Channel上的线程会得到AsynchronousCloseException异常。
以上这段中提到的Channel对象需要InterruptibleChannel接口,但FileChannel和SocketChannel等大多数类都完成了对这个interface的实现。
2. Scatter/Gather集与散
Scatter的意思是分散,Gather的意思是聚集。我们注意到在上面的类层次结构图中,除了ByteChannel外,各Channel类还都实现了两个接口,分别是:
ScatteringByteChannel
GatheringByteChannel
对于这两个接口,这里不做过多描述。也许我们通过接口中约定的方法,就大概知道他们的作用了。
1
2
3
4
5
6
7
8
9
10
public interface ScatteringByteChannel extends ReadableByteChannel
{
public long read (ByteBuffer [] dsts) throws IOException;
public long read (ByteBuffer [] dsts, int offset, int length) throws IOException;
}
public interface GatheringByteChannel extends WritableByteChannel
{
public long write(ByteBuffer[] srcs) throws IOException;
public long write(ByteBuffer[] srcs, int offset, int length) throws IOException;
}
实际上就是对读写操作进行顺序多Buffer的支持。
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
Java NIO中的通道Channel(一)通道基础
攻破JAVA NIO技术壁垒
java的IO模型
Android NIO非阻塞包
JAVA NIO 简介
Selector的使用-服务器端code
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服