Channel
接口Channel
一种通信机制的抽象,一个Channel
对应操作系统底层的一个资源句柄,也就是当前线程的通信对象
功能对应的接口有
-
可读(
ReadableByteChannel
),可写(WritableByteChannel
) -
可随机读写(
SeekableByteChannel
) -
Scatter/Gather(
ScatteringByteChannel/GatheringByteChannel
)能够一次性写入多个Buffer(Gather),读取多个Buffer的序列(Scatter) -
可中断关闭(
InterruptibleChannel
)通道处于IO阻塞的状态时能被另一个线程关闭
和 可IO复用(SelectableChannel
)能够检测通道的状态是否为阻塞,能够设置当前通道为非阻塞模式,能够借助Selector注册和检测事件(非阻塞模式下)SelectableChannel
继承自InterruptibleChannel
-
可网络通信(
NetworkChannel
)和可广播(MulticastChannel
)
具体实现有
FileChannel
实现了 可读,可写,可随机读写,可中断关闭
SocketChannel
实现了 可读,可写,Scatter/Gather,可IO复用,可网络通信
DatagramChannel
相比于SocketChannel
增加了可广播
Pipe
相当于命名管道,它最为特殊,聚合了两个通道SourceChannel
和SinkChannel
SinkChannel
实现了可写,Gather,可IO复用SourceChannel
实现了可读,Scatter,可IO复用
Pipe可以用于多线程之间的通信,因为通道的实现通常是两个内核缓冲区,所以向通道中读写数据时是阻塞的(缓冲区已满不能写,缓冲区已空不能读),所以SourceChannel
和SinkChannel
相当于一个阻塞队列的队首和队尾,可以一个线程向SinkChannel
中写,另一个从SourceChannel
中读,并且可以利用Selector
作IO复用,实现异步通信
注意一点,就是对非阻塞模式的理解
非阻塞模式就是当前读不到数据或者暂时不能写就返回失败
只有非阻塞模式下才能检测到事件,因为IO复用最基本的思想就是轮询检测能否读写成功
epoll模型对此做了改进,只检测通道(fd)对应的注册事件,使用共享内存(mmap())传递事件消息events(对应SelectedKey),事件消息关联了通道和复用器,保存了通道的关注事件和就绪事件,甚至还可以设置事件消息的处理者(attachment)
Buffer
用于Channel
读写的缓冲器,实质是对字节数组的封装
File/Path
File/Path
的本质其实都是文件路径,也就是一个字符串,是对可操作的文件对象的一个抽象,可以借助这个路径获取与文件相关的属性(依靠内核的目录树和inode节点的数据)
当我们读写文件时的输入输出流操作和工具类方法的实现实际都在使用文件路径关联到对应的FileChannel进行读写
IO Stream
io流分为了输入输出流,随机读写流,管道流
输入输出流进一步分为了字节流和字符流
流与上述文件路径的区别就是,打开一个流是实际地打开了一个文件,会在内核中创建对应的结构(线程中地资源句柄和内核中的打开文件列表项),这些结构会记录文件读写的位置,文件锁,引用计数等
RandomAccessFile
任意读写流,这个名字起的不太好,会与上述的File混淆,实际上这不是一个文件路径,而是一个类似c中的文件指针的东西,可以设置文件读写位置指针,进行非顺序的读写
管道流
PipedInputStream/PipedOutputStream
类似于linux的匿名管道,与nio包中的Pipe相比
没有可中断关闭和IO复用的功能
底层实现也有固定大小的缓冲区,所以读写也是阻塞的,可以用来实现多线程通信
最常见的用法是连接某个输出流到某个输入流,只能读取一次
Files
工具类
使用FIles文件可以用工厂方法创建某个文件的输入输出流
进行文件的相关操作(复制,移动,获取文件属性等)
总结
channel本身就是一种底层通信机制的抽象,输入输出流是建立在这之上的一种高层的抽象