`
zachary.guo
  • 浏览: 486777 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

NIO - Channel Foundation

    博客分类:
  • NIO
阅读更多
        首先,我们来更近距离地看一下基本的 Channel 接口。Channel 接口的完整源码:
package java.nio.channels;

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

        与缓冲区不同,通道 API 主要由接口指定。不同的操作系统上通道实现(Channel Implementation)会有根本性的差异,所以通道 API 仅仅描述了可以做什么。因此很自然地,通道实现经常使用操作系统的本地代码。你可以从顶层的 Channel 接口看到,对所有通道来说只有两种共同的操作:检查一个通道是否打开(isOpen())和关闭一个打开的通道(close())。

        从 Channel 的类层次关系上可以看出,有两个类并不位于 java.nio.channels 包中。这两个类是 AbstractInterruptibleChannel 和  AbstractSelectableChannel,位于 java.nio.channel.spi,它们分别为可中断的(interruptible)和可选择的(selectable)的通道实现提供所需的常用方法。尽管描述通道行为的接口都是在 java.nio.channels 包中定义的,不过具体的通道实现却都是从 java.nio.channels.spi 中的类引申来的。这使得他们可以访问受保护的方法,而这些方法普通的通道用户永远都不会调用。作为通道的一个使用者,你可以放心地忽视 SPI 包中包含的中间类。

        从 Channel 接口引申出的其他接口都是面向字节的子接口,包括 WritableByteChannel 和 ReadableByteChannel。这也正好印证了通道只能在字节缓冲区上操作。同时也表明其它数据类型的通道也可以从 Channel 接口引申而来。这是一种很好的类设计,不过非字节实现是不可能的,因为操作系统都是以字节的形式实现底层 I/O 接口的。

    ◇ 打开通道
        通道是访问 I/O 服务的导管。I/O 可以分为广义的两大类别:File I/O 和 Stream I/O,那么就对应于文件(file)通道和套接字(socket)通道。从通道类层次关系我们会发现有一个 FileChannel 类和三个 socket 通道类:SocketChannel、ServerSocketChannel 和 DatagramChannel。创建并打开通道的一些示例:
SocketChannel sc = SocketChannel.open();
sc.connect (new InetSocketAddress ("localhost", serverPort));

ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.socket().bind (new InetSocketAddress(serverPort));

DatagramChannel dc = DatagramChannel.open();

RandomAccessFile raf = new RandomAccessFile("somefile", "r");
FileChannel fc = raf.getChannel();

        java.net 的 socket 类也有新的 getChannel()方法。这些方法虽然能返回一个相应的 socket 通道对象,但它们却并非新通道的来源,RandomAccessFile.getChannel() 方法才是。只有在已经有通道存在的时候,它们才返回与一个 socket 关联的通道,它们永远不会创建新通道。

        通道可以以多种方式创建。Socket 通道有可以直接创建新 socket 通道的工厂方法,但是一个 FileChannel 对象却只能通过在一个打开的 RandomAccessFile、FileInputStream 或 FileOutputStream 对象上调用 getChannel() 方法来获取。你不能直接创建一个 FileChannel 对象。

    ◇ 使用通道
        在 NIO - Buffer 这篇文章中,我们了解到,通道将数据传输给 ByteBuffer 对象或者从 ByteBuffer 对象获取数据进行传输。ReadableByteChannel,通道是可读的,因此一定是从 Channel 读。读到哪里?读到 ByteBuffer;WritableByteChannel,通道是可写的,因此一定是写入 channel。写的内容来自哪里?来自 ByteBuffer。看看关于通道的几个子接口:
public interface ReadableByteChannel extends Channel {
    // Reads a sequence of bytes from this channel into the given buffer.
    public int read(ByteBuffer dst) throws IOException;
}

public interface WritableByteChannel extends Channel {
    // Writes a sequence of bytes to this channel from the given buffer.
    public int write(ByteBuffer src) throws IOException;
}

public interface ByteChannel extends ReadableByteChannel, WritableByteChannel {}

        通道可以是单向(unidirectional)或者双向的(bidirectional)。一个 channel 类可能实现定义 read() 方法的 ReadableByteChannel 接口,而另一个 channel 类也许实现 WritableByteChannel 接口以提供 write() 方法。实现这两种接口其中之一的类都是单向的,只能在一个方向上传输数据。如果一个类同时实现这两个接口,那么它是双向的,可以双向传输数据。

        一个文件可以在不同的时候以不同的权限打开。从 FileInputStream 对象的 getChannel() 方法获取的 FileChannel 对象是只读的,不过从接口声明的角度来看却是双向的,因为 FileChannel 实现 ByteChannel 接口。在这样一个通道上调用 write() 方法将抛出未经检查的 NonWritableChannelException 异常,因为 FileInputStream 对象总是以 read-only 的权限打开文件。

        通道会连接一个特定 I/O 服务且通道实例(channel instance)的性能受它所连接的 I/O 服务的特征限制。一个连接到只读文件的 Channel 实例不能进行写操作,即使该实例所属的类可能有 write() 方法。因此,程序员需要知道通道是如何打开的,避免试图尝试一个底层 I/O 服务不允许的操作。避免尝试底层不支持的操作:
// A ByteBuffer named buffer contains data to be written
FileInputStream input = new FileInputStream (fileName);
FileChannel channel = input.getChannel();
// This will compile but will throw an IOException for the underlying file is read-only
channel.write(buffer);

    ◇ 关闭通道
        与缓冲区不同,通道不能被重复使用。一个打开的通道即代表与一个特定 I/O 服务的特定连接并封装该连接的状态。当通道关闭时,那个连接会丢失,然后通道将不再连接任何东西。

        调用通道的 close() 方法时,可能会导致在通道关闭底层 I/O 服务的过程中线程暂时阻塞,哪怕该通道处于非阻塞模式。通道关闭时的阻塞行为(如果有的话)高度取决于操作系统或者文件系统的。

        在一个通道上多次调用 close() 方法是没有坏处的,但是如果第一个线程在 close() 方法中阻塞,那么在它完成关闭通道之前,任何其他调用 close() 方法都会阻塞。后续在该已关闭的通道上调用 close() 不会产生任何操作,只会立即返回。

        可以通过 isOpen() 方法来测试通道的开放状态。如果返回 true 值,那么该通道可以使用。如果返回 false 值,那么该通道已关闭,不能再被使用。尝试进行任何需要通道处于开放状态作为前提的操作,如读、写等都会导致 ClosedChannelException 异常。

    ◇ Scatter/Gather
        通道提供了一种被称为 Scatter/Gather 的重要新功能(有时也被称为矢量 I/O)。Scatter/Gather 是一个简单却强大的概念,它是指在多个缓冲区上实现一个简单的 I/O 操作。

        对于一个 write 操作而言,数据是从几个缓冲区按顺序抽取(称为 gather)并沿着通道发送的。缓冲区本身并不需要具备这种 gather 的能力(通常它们也没有此能力)。该 gather 过程的效果就好比全部缓冲区的内容被连结起来,并在发送数据前存放到一个大的缓冲区中。

        对于 read 操作而言,从通道读取的数据会按顺序被分散(称为 scatter)到多个缓冲区,将每个缓冲区填满直至通道中的数据或者缓冲区的最大空间被消耗完。

        大多数现代操作系统都支持本地矢量 I/O(native vectored I/O)。当你在一个通道上请求一个 Scatter/Gather 操作时,该请求会被翻译为适当的本地调用来直接填充或抽取缓冲区。这是一个很大的进步,因为减少或避免了缓冲区拷贝和系统调用。Scatter/Gather 应该使用直接的 ByteBuffers 以从本地 I/O 获取最大性能优势。scatter 描述如何扩展读操作,gather 描述如何扩展写操作:
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;
}

        数据从缓冲区阵列引用的每个缓冲区中 gather 并被组合成沿着通道发送的字节流。

                                                   从四个缓冲区 gather 后的数据写入通道

        从通道传输来的数据被 scatter 到所列缓冲区,依次填充每个缓冲区(从缓冲区的 position 处开始到 limit 处结束)。这里显示的 position 和 limit 值是读(从 channel 读,即写入 buffer)操作开始之前的。

                                                   从通道读数据 scatter 到四个缓冲区

        带 offset 和 length 参数版本的 read() 和 write() 方法使得我们可以使用缓冲区阵列的子集缓冲区。这里的 offset 值指哪个缓冲区将开始被使用,而不是指数据的 offset。这里的 length 参数指示要使用的缓冲区数量。offset 和 length 意指从哪个缓冲区开始使用,并使用多少个缓冲区:
// 假设我们有一个五元素的 fiveBuffers 阵列,它已经被初始化并引用了五个缓冲区
ByteBuffer[] fiveBuffers = ...
// 下面的代码将会将第二个、第三个和第四个缓冲区的内容写入通道
int bytesRead = channel.write(fiveBuffers, 1, 3);

        使用得当的话,Scatter/Gather 会是一个极其强大的工具。它允许你委托操作系统来完成辛苦活:将读取到的数据分开存放到多个存储桶(bucket)或者将不同的数据区块合并成一个整体。这是一个巨大的成就,因为操作系统已经被高度优化来完成此类工作了。它节省了你来回移动数据的工作,也就避免了缓冲区拷贝和减少了你需要编写、调试的代码数量。
  • 大小: 51.5 KB
  • 大小: 45.8 KB
分享到:
评论

相关推荐

    基于微信小程序的在线办公小程序答辩PPT.pptx

    基于微信小程序的在线办公小程序答辩PPT.pptx

    机器学习(预测模型):2000年至2015年期间193个国家的预期寿命和相关健康因素的数据

    这个数据集来自世界卫生组织(WHO),包含了2000年至2015年期间193个国家的预期寿命和相关健康因素的数据。它提供了一个全面的视角,用于分析影响全球人口预期寿命的多种因素。数据集涵盖了从婴儿死亡率、GDP、BMI到免疫接种覆盖率等多个维度,为研究者提供了丰富的信息来探索和预测预期寿命。 该数据集的特点在于其跨国家的比较性,使得研究者能够识别出不同国家之间预期寿命的差异,并分析这些差异背后的原因。数据集包含22个特征列和2938行数据,涉及的变量被分为几个大类:免疫相关因素、死亡因素、经济因素和社会因素。这些数据不仅有助于了解全球健康趋势,还可以辅助制定公共卫生政策和社会福利计划。 数据集的处理包括对缺失值的处理、数据类型转换以及去重等步骤,以确保数据的准确性和可靠性。研究者可以使用这个数据集来探索如教育、健康习惯、生活方式等因素如何影响人们的寿命,以及不同国家的经济发展水平如何与预期寿命相关联。此外,数据集还可以用于预测模型的构建,通过回归分析等统计方法来预测预期寿命。 总的来说,这个数据集是研究全球健康和预期寿命变化的宝贵资源,它不仅提供了历史数据,还为未来的研究和政策制

    基于微信小程序的“健康早知道”微信小程序答辩PPT.pptx

    基于微信小程序的“健康早知道”微信小程序答辩PPT.pptx

    基于微信小程序的电影交流平台答辩PPT.pptx

    基于微信小程序的电影交流平台答辩PPT.pptx

    计算机字符编码GB18030.PDF

    计算机字符编码GB18030

    Hive 操作基础(进阶版)多级分区数据文件2

    Hive 操作基础(进阶版)多级分区数据文件2

    基于java的贫困生管理系统答辩PPT.pptx

    基于java的贫困生管理系统答辩PPT.pptx

    pandas-2.1.4-cp312-cp312-win_amd64.zip

    pandas whl安装包,对应各个python版本和系统(具体看资源名字),找准自己对应的下载即可! 下载后解压出来是已.whl为后缀的安装包,进入终端,直接pip install pandas-xxx.whl即可,非常方便。 再也不用担心pip联网下载网络超时,各种安装不成功的问题。

    TA_Lib轮子无需编译-TA_Lib-0.4.18-cp38-cp38-win32.whl.zip

    TA_lib库(whl轮子),直接pip install安装即可,下载即用,非常方便,各个python版本对应的都有。 使用方法: 1、下载下来解压; 2、确保有python环境,命令行进入终端,cd到whl存放的目录,直接输入pip install TA_lib-xxxx.whl就可以安装,等待安装成功,即可使用! 优点:无需C++环境编译,下载即用,方便

    课设毕设基于SpringBoot+Vue的瑜伽体验课预约系统源码可运行.zip

    本压缩包资源说明,你现在往下拉可以看到压缩包内容目录 我是批量上传的基于SpringBoot+Vue的项目,所以描述都一样;有源码有数据库脚本,系统都是测试过可运行的,看文件名即可区分项目~ |Java|SpringBoot|Vue|前后端分离| 开发语言:Java 框架:SpringBoot,Vue JDK版本:JDK1.8 数据库:MySQL 5.7+(推荐5.7,8.0也可以) 数据库工具:Navicat 开发软件: idea/eclipse(推荐idea) Maven包:Maven3.3.9+ 系统环境:Windows/Mac

    tornado-6.2b2.tar.gz

    tornado-6.2b2.tar.gz

    javawe论坛项目 原生技术

    javawe论坛项目 原生技术

    tornado-6.2b1-cp310-cp310-macosx_10_9_universal2.whl

    tornado-6.2b1-cp310-cp310-macosx_10_9_universal2.whl

    基于司机信用评价的货运管理系统(springboot+vue+mysql+说明文档).zip

    随着物流行业的快速发展,货运管理变得愈发重要。为了提高货运效率,确保货物安全,我们开发了这款基于司机信用评价的货运管理系统。 该系统主要包含了货物信息管理、订单评价管理、货主管理等多个功能模块。在货物信息管理模块中,用户可以查看和管理货物的详细信息,包括货物名称、规格、装车状态、运输状态以及卸货状态等,方便用户随时掌握货物的动态。 订单评价管理模块是该系统的核心之一,它允许货主对司机的服务进行评价,系统会根据评价数据对司机进行信用评分。这一功能不仅有助于提升司机的服务质量,还能为货主提供更加可靠的货运选择。 此外,货主管理模块提供了货主信息的录入、修改和查询等功能,方便用户管理自己的货主资料。系统界面简洁明了,以蓝色为主色调,设计现代且专业,为用户提供了良好的使用体验。 通过该系统,用户可以轻松实现货物信息的查看和管理,对司机的服务进行评价,提高货运效率和服务质量。同时,系统也为司机提供了一个展示自我、提升信用的平台,有助于推动物流行业的健康发展。

    毕业生交流学习平台 SSM毕业设计 附带论文.zip

    毕业生交流学习平台 SSM毕业设计 附带论文 启动教程:https://www.bilibili.com/video/BV1GK1iYyE2B

    基于java的广场舞团答辩PPT.pptx

    基于java的广场舞团答辩PPT.pptx

    基于java的基于SSM的校园音乐平台答辩PPT.pptx

    基于java的基于SSM的校园音乐平台答辩PPT.pptx

    安装包JIRATimeSLA

    Jira插件安装包

    【java毕业设计】基于图像识别与分类的中国蛇类识别系统源码(springboot+vue+mysql+说明文档).zip

    项目经过测试均可完美运行! 环境说明: 开发语言:java jdk:jdk1.8 数据库:mysql 5.7+ 数据库工具:Navicat11+ 管理工具:maven 开发工具:idea/eclipse

    tornado-6.2b2-cp37-abi3-win_amd64.whl

    tornado-6.2b2-cp37-abi3-win_amd64.whl

Global site tag (gtag.js) - Google Analytics