通俗编程——白话NIO之Channel

Channel简介

在标准的IO当中,都是基于字节流/字符流进行操作的,而在NIO中则是是基于Channel和Buffer进行操作,其中的Channel的虽然模拟了流的概念,实则大不相同。

区别 Stream Channel
支持异步 不支持 支持
是否可双向传输数据 不能,只能单向 可以,既可以从通道读取数据,也可以向通道写入数据
是否结合Buffer使用 必须结合Buffer使用
性能 较低 较高

Channel用于在字节缓冲区和位于通道另一侧的实体(通常是文件或者套接字)之间以便有效的进行数据传输。借助通道,可以用最小的总开销来访问操作系统本身的I/O服务。

通道必须结合Buffer使用,不能直接向通道中读/写数据,其结构如下图:
此处输入图片的描述

通道的API主要由接口指定,在不同的操作系统上有不同的实现。该接口如下所示:

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

从接口看来,所有的通道都有这两种操作:检查通道的开启状态和关闭通道。从Channel接口引申出的其他接口都是面向字节的子接口,也就是说通道本质上都是对自己缓冲区进行操作的。


Channel的分类

广义上来说通道可以被分为两类:File I/O和Stream I/O,也就是文件通道和套接字通道。如果分的更细致一点则是:

  • FileChannel 从文件读写数据
  • SocketChannel 通过TCP读写网络数据
  • ServerSocketChannel 可以监听新进来的TCP连接,并对每个链接创建对应的SocketChannel
  • DatagramChannel 通过UDP读写网络中的数据
  • Pipe

对于Socket通道来说存在直接创建新Socket通道的方法,而对于文件通道来说,升级之后的FileInputStream、FileOutputStream和RandomAccessFile提供了getChannel()方法来获取通道。需要注意的是java.net包中的socket类也存在getChannel()方法,但他返回的并非新通道。

通道既可以是单向的也可以是双向的。只实现ReadableByteChannel接口中的read()方法或者只实现WriteableByteChannel接口中的write()方法的通道皆为单向通道,同时实现ReadableByteChannel和WriteableByteChannel为双向通道,比如ByteChannel。对于socket通道来说,它们一直是双向的,而对于FileChannel来说,它同样实现了ByteChannel,但是我们知道通过FileInputStream的getChannel()获取的FileChannel只具有文件的只读权限,那此时的在该通道调用write()会出现什么情况?不出意外的抛出了NonWriteChannelException异常。
通过以上,我们得出结论:通道都与特定的I/O服务挂钩,并且通道的性能受限于所连接的I/O服务的性质。

通道的工作模式有两种:阻塞或非阻塞。在非阻塞模式下,调用的线程不会休眠,请求的操作会立刻返回结果;在阻塞模式下,调用的线程会产生休眠。另外除FileChannel不能运行在非阻塞模式下,其余的通道都可阻塞运行也可以以非阻塞的方式运行。

另外从SelectableChannel引申出的类可以和支持有条件选择的Selector结合使用,进而充分利用多路复用的I/O(multiplexed I/O)来提高性能.对于Socket通道类来说,通常与Selector共同使用以提高性能。需要注意的是通道不能被重复使用,一个打开的通道代表着与一个特定I/O服务进行连接并封装了该连接的状态,通道一旦关闭,该连接便会断开。通道的close()比较特殊,无论在通道时在阻塞模式下还是非阻塞模式下,由于close()方法的调用而导致底层I/O的关闭都可能会造成线程的暂时阻塞。在一个已关闭的通道上调用close()并没有任何意义,只会立即返回。


1. 文件通道(FileChannel)

Java NIO中的FileChannel是一个连接到文件的通道。可以通过文件通道读写文件。文件通道总是阻塞式的,因此FileChannel无法设置为非阻塞模式.

同大多数 I/O 相关的类一样,FileChannel是一个反映Java虚拟机外部一个具体对象的抽象。FileChannel类保证同一个Java虚拟机上的所有实例看到的某个文件的视图均是一致的,但是Java虚拟机却不能对超出它控制范围的因素提供担保。通过一个FileChannel实例看到的某个文件的视图与通过一个外部的非Java进程看到的该文件的视图可能一致,也可能不一致。一般而言,由运行在不同Java虚拟机上的FileChannel对象发起的对某个文件的并发访问和由非 Java 进程发起的对该文件的并发访问是一致的。

1. FileChannel的使用


  1. 创建FileChannel

升级之后之后的InputStream,OutputStream,RandomAccessFile可通过getChannel()方法获取FileChannel实例

  • 从FileChannel读取数据

    通过调用Channel的read()方法可将Channel中的数据读取到Buffer中

  • 想FileChannel写入数据

    通过Channel的write()方法可想FileChannel写入数据

  • 关闭FileChannel

    用完的FileChannel需要通过Channel的close()关闭。

  • 获取当前position或设置position的值

    通过position()方法获取当前FileChannel的当前位置,通过position(longpos)设置FileChannel的当前位置。和底层的文件描述符一样,每个FileChannel都有一个file position的概念。该position值决定了从文件的哪里开始读或者写(你可以把它当作我们编辑时出现的编辑光标),该position直接从底层文件描述符中获取,该position同时被作为通道引用获取来源的文件对象共享,在任何时候修改该position的值对其他对象也是可见的。可以通过position()来获取当前的”file position”,也可以通过position(newPosition)来设置。

  • 通过FileChannel实例的size()可获取FileChannel关联文件的大小

  • 通过truncate()方法截取所关联文件的指定长度。
  • 通过force(boolean metaData)强制将通道中未写入磁盘的数据立刻写入到磁盘。metaData为true时,表示将文件的元数据一同写入到磁盘。

    在现在操作系统中,其内部的文件系统多带有缓存数据和延迟磁盘文件更新的特点,如果在本地文件系统中调用force

  • 9
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值