`

全面解读Java NIO工作原理(3)

    博客分类:
  • Java
阅读更多

 

JDK 1.4 中引入的新输入输出 (NIO) 库在标准 Java 代码中提供了高速的、面向块的 I/O。本实用教程从高级概念到底层的编程细节,非常详细地介绍了 NIO 库。您将学到诸如缓冲区和通道这样的关键 I/O 元素的知识,并考察更新后的库中的标准 I/O 是如何工作的。您还将了解只能通过 NIO 来完成的工作,如异步 I/O 和直接缓冲区。

Sky:

 

◆  关于缓冲区的更多内容

概  述

到目前为止,您已经学习了使用缓冲区进行日常工作所需要掌握的大部分内容。

我们的例子没怎么超出标准的读/写过程种类,

在原来的 I/O 中可以像在 NIO 中一样容易地实现这样的标准读写过程。

本节将讨论使用缓冲区的一些更复杂的方面,比如缓冲区分配、

包装和分片。我们还会讨论 NIO 带给 Java 平台的一些新功能。

您将学到如何创建不同类型的缓冲区以达到不同的目的,

如可保护数据不被修改的 只读 缓冲区,和直接映射到底层操作系统缓冲区的

直接 缓冲区。我们将在本节的最后介绍如何在 NIO 中创建内存映射文件。

缓冲区分配和包装

在能够读和写之前,必须有一个缓冲区。要创建缓冲区,

您必须 分配 它。我们使用静态方法 allocate() 来分配缓冲区:

  1. ByteBuffer buffer = ByteBuffer.allocate( 1024 ); 

allocate() 方法分配一个具有指定大小的底层数组,

并将它包装到一个缓冲区对象中 ― 在本例中是一个 ByteBuffer。

您还可以将一个现有的数组转换为缓冲区,如下所示:

  1. byte array[] = new byte[1024];  
  2. ByteBuffer buffer = ByteBuffer.wrap( array ); 

本例使用了 wrap() 方法将一个数组包装为缓冲区。必须非常小心地进行这类操作。

一旦完成包装,底层数据就可以通过缓冲区或者直接访问。

缓冲区分片

slice() 方法根据现有的缓冲区创建一种 子缓冲区 。也就是说,

它创建一个新的缓冲区,新缓冲区与原来的缓冲区的一部分共享数据。

使用例子可以最好地说明这点。让我们首先创建一个长度为 10 的 ByteBuffer:

  1. ByteBuffer buffer = ByteBuffer.allocate( 10 ) 

然后使用数据来填充这个缓冲区,在第 n 个槽中放入数字 n:

  1. for (int i=0; i<buffer.capacity(); ++i) {  
  2.      buffer.put( (byte)i );  

现在我们对这个缓冲区 分片 ,以创建一个包含槽 3 到槽 6 的子缓冲区。

在某种意义上,子缓冲区就像原来的缓冲区中的一个 窗口 。

窗口的起始和结束位置通过设置 position 和 limit 值来指定,然后调用 Buffer 的 slice() 方法:

  1. buffer.position( 3 );  
  2. buffer.limit( 7 );  
  3. ByteBuffer slice = buffer.slice(); 

片 是缓冲区的 子缓冲区 。不过, 片段 和 缓冲区 共享同一个底层数据数组,

我们在下一节将会看到这一点。

缓冲区份片和数据共享

我们已经创建了原缓冲区的子缓冲区,并且我们知道缓冲区和子缓冲区共享同一个底层数据数组。

让我们看看这意味着什么。

 

我们遍历子缓冲区,将每一个元素乘以 11 来改变它。例如,5 会变成 55。

  1. for (int i=0; i<slice.capacity(); ++i) {  
  2.      byte b = slice.get( i );  
  3.      b *= 11;  
  4.      slice.put( i, b );  

最后,再看一下原缓冲区中的内容:

  1. buffer.position( 0 );  
  2. buffer.limit( buffer.capacity() );  
  3.  while (buffer.remaining()>0) {  
  4.      System.out.println( buffer.get() );  

结果表明只有在子缓冲区窗口中的元素被改变了:

$ java SliceBuffer 
0 
1 
2 
33 
44 
55 
66 
7 
8 
9

缓冲区片对于促进抽象非常有帮助。可以编写自己的函数处理整个缓冲区

,而且如果想要将这个过程应用于子缓冲区上,您只需取主缓冲区的一个片,

并将它传递给您的函数。这比编写自己的函数来取额外的参数以指定要对缓冲区的哪一部分进行操作更容易。

只读缓冲区

只读缓冲区非常简单 ― 您可以读取它们,但是不能向它们写入。

可以通过调用缓冲区的 asReadOnlyBuffer() 方法,

将任何常规缓冲区转换为只读缓冲区,这个方法返回一个与原缓冲区完全相同的缓冲区(并与其共享数据),

只不过它是只读的。

只读缓冲区对于保护数据很有用。在将缓冲区传递给某个对象的方法时,

您无法知道这个方法是否会修改缓冲区中的数据。创建一个只读的缓冲区可以 保证 该缓冲区不会被修改。

不能将只读的缓冲区转换为可写的缓冲区。

直接和间接缓冲区

另一种有用的 ByteBuffer 是直接缓冲区。 直接缓冲区 是为加快 I/O 速度,

而以一种特殊的方式分配其内存的缓冲区。

实际上,直接缓冲区的准确定义是与实现相关的。Sun 的文档是这样描述直接缓冲区的:

给定一个直接字节缓冲区,Java 虚拟机将尽最大努力直接对它执行本机 I/O 操作。

也就是说,它会在每一次调用底层操作系统的本机 I/O 操作之前(或之后),

尝试避免将缓冲区的内容拷贝到一个中间缓冲区中(或者从一个中间缓冲区中拷贝数据)。

您可以在例子程序 FastCopyFile.java 中看到直接缓冲区的实际应用,

这个程序是 CopyFile.java 的另一个版本,它使用了直接缓冲区以提高速度。

还可以用内存映射文件创建直接缓冲区。

内存映射文件 I/O

内存映射文件 I/O 是一种读和写文件数据的方法,它可以比常规的基于流或者基于通道的 I/O 快得多。

内存映射文件 I/O 是通过使文件中的数据神奇般地出现为内存数组的内容来完成的

。这其初听起来似乎不过就是将整个文件读到内存中,但是事实上并不是这样。

一般来说,只有文件中实际读取或者写入的部分才会送入(或者 映射 )到内存中。

内存映射并不真的神奇或者多么不寻常。

现代操作系统一般根据需要将文件的部分映射为内存的部分,从而实现文件系统。

Java 内存映射机制不过是在底层操作系统中可以采用这种机制时,提供了对该机制的访问。

尽管创建内存映射文件相当简单,但是向它写入可能是危险的。

仅只是改变数组的单个元素这样的简单操作,就可能会直接修改磁盘上的文件。

修改数据与将数据保存到磁盘是没有分开的。

将文件映射到内存

了解内存映射的最好方法是使用例子。在下面的例子中

,我们要将一个 FileChannel (它的全部或者部分)映射到内存中。

为此我们将使用 FileChannel.map() 方法。下面代码行将文件的前 1024 个字节映射到内存中:

  1. MappedByteBuffer mbb = fc.map( FileChannel.MapMode.READ_WRITE,     01024 ); 

map() 方法返回一个 MappedByteBuffer,它是 ByteBuffer 的子类。

因此,您可以像使用其他任何 ByteBuffer 一样使用新映射的缓冲区,操作系统会在需要时负责执行行映射。

◆  分散和聚集

概  述

分散/聚集 I/O 是使用多个而不是单个缓冲区来保存数据的读写方法。

一个分散的读取就像一个常规通道读取,只不过它是将数据读到一个缓冲区数组中而

不是读到单个缓冲区中。同样地,一个聚集写入是向缓冲区数组而不是向单个缓冲区写入数据。

分散/聚集 I/O 对于将数据流划分为单独的部分很有用,这有助于实现复杂的数据格式。

分散/聚集 I/O

通道可以有选择地实现两个新的接口: ScatteringByteChannel 和 GatheringByteChannel。一个 ScatteringByteChannel 是一个具有两个附加读方法的通道:

• long read( ByteBuffer[] dsts );

• long read( ByteBuffer[] dsts, int offset, int length );

这些 long read() 方法很像标准的 read 方法,只不过它们不是取单个缓冲区而是取一个缓冲区数组。

在 分散读取 中,通道依次填充每个缓冲区。填满一个缓冲区后,它就开始填充下一个。

在某种意义上,缓冲区数组就像一个大缓冲区。

分散/聚集的应用

分散/聚集 I/O 对于将数据划分为几个部分很有用。例如,您可能在编写一个使用消息对象的网络应用程序,

每一个消息被划分为固定长度的头部和固定长度的正文。您可以创建一个刚好可以容纳头部的缓冲区

和另一个刚好可以容难正文的缓冲区。当您将它们放入一个数组中并使用分散读取来向它们读入消息时,

头部和正文将整齐地划分到这两个缓冲区中。

我们从缓冲区所得到的方便性对于缓冲区数组同样有效。因为每一个缓冲区都跟踪自己还可以接受多少数据,

所以分散读取会自动找到有空间接受数据的第一个缓冲区。在这个缓冲区填满后,它就会移动到下一个缓冲区。

聚集写入

聚集写入 类似于分散读取,只不过是用来写入。它也有接受缓冲区数组的方法:

• long write( ByteBuffer[] srcs );

• long write( ByteBuffer[] srcs, int offset, int length );

聚集写对于把一组单独的缓冲区中组成单个数据流很有用。为了与上面的消息例子保持一致,

您可以使用聚集写入来自动将网络消息的各个部分组装为单个数据流,以便跨越网络传输消息。

从例子程序 UseScatterGather.java 中可以看到分散读取和聚集写入的实际应用。

◆  文件锁定

概  述

文件锁定初看起来可能让人迷惑。它 似乎 指的是防止程序或者用户访问特定文件。

事实上,文件锁就像常规的 Java 对象锁 ― 它们是 劝告式的(advisory) 锁。

它们不阻止任何形式的数据访问,相反,它们通过锁的共享和获取赖允许系统的不同部分相互协调。

您可以锁定整个文件或者文件的一部分。如果您获取一个排它锁,那么其他人就不

能获得同一个文件或者文件的一部分上的锁。如果您获得一个共享锁,那么其他人可

以获得同一个文件或者文件一部分上的共享锁,但是不能获得排它锁。

文件锁定并不总是出于保护数据的目的。例如,您可能临时锁定一个文件以保证特定的写操作成为原子的,而不会有其他程序的干扰。

大多数操作系统提供了文件系统锁,但是它们并不都是采用同样的方式。有

些实现提供了共享锁,而另一些仅提供了排它锁。事实上,

有些实现使得文件的锁定部分不可访问,尽管大多数实现不是这样的。

在本节中,您将学习如何在 NIO 中执行简单的文件锁过程,

我们还将探讨一些保证被锁定的文件尽可能可移植的方法。

锁定文件

要获取文件的一部分上的锁,您要调用一个打开的 FileChannel 上的 lock() 方法。

注意,如果要获取一个排它锁,您必须以写方式打开文件。

  1. RandomAccessFile raf = new RandomAccessFile( "usefilelocks.txt""rw" );  
  2. FileChannel fc = raf.getChannel();  
  3. FileLock lock = fc.lock( start, end, false ); 

在拥有锁之后,您可以执行需要的任何敏感操作,然后再释放锁:

  1. lock.release(); 

在释放锁后,尝试获得锁的其他任何程序都有机会获得它。

本小节的例子程序 UseFileLocks.java 必须与它自己并行运行。

这个程序获取一个文件上的锁,持有三秒钟,然后释放它。如果同时运行这个程序的多个实例

,您会看到每个实例依次获得锁。

文件锁定可能是一个复杂的操作,特别是考虑到不同的操作系统是以不同的方式实现锁这一事实。

下面的指导原则将帮助您尽可能保持代码的可移植性:

• 只使用排它锁。

• 将所有的锁视为劝告式的(advisory)。

分享到:
评论

相关推荐

    基于S7-300PLC与MCGS6.2的饮料罐装生产线自动化控制系统设计,包含仿真、程序、IO表与电气原理,实现自动操作、灌装报警及瓶数记录功能 ,基于PLC的饮料罐装生产线控制系统设计 S7-30

    基于S7-300PLC与MCGS6.2的饮料罐装生产线自动化控制系统设计,包含仿真、程序、IO表与电气原理,实现自动操作、灌装报警及瓶数记录功能。,基于PLC的饮料罐装生产线控制系统设计。 S7-300PLC MCGS6.2仿真 仿真,程序,IO表,电气原理图,6500字说明。 实现功能有: (1)系统通过开关设定为自动操作模式,一旦启动,则传送带的驱动电机启动并一直保持到停止开关动作或罐装设备下的传感器检测到一个瓶子时停止;瓶子装满饮料后,传送带驱动电机必须自动启动,并保持到又检测到一个瓶子或停止开关动作。 (2)当瓶子定位在灌装设备下时,停顿1秒,罐装设备开始工作,灌装过程为5秒钟,罐装过程应有报警显示,5秒后停止并不再显示报警。 (2)用两个传感器和若干个加法器检测并记录空瓶数和满瓶数,一旦系统启动,必须记录空瓶和满瓶数,设最多不超过99999999瓶。 (4)可以手动对计数器清零(复位)。 ,关键词:S7-300PLC; MCGS6.2仿真; 传送带驱动电机; 传感器检测; 瓶装; 空瓶数; 满瓶数; 报警显示; 自动操作模式; 灌装设备。,基于S7-300PLC的饮料罐装

    python加密货币时间序列预测源码+数据集-最新出炉.zip

    python加密货币时间序列预测源码+数据集-最新出炉 加密货币分析: 对各种加密货币的数据进行分析和研究。可能会使用到从各种来源收集的数据,包括但不限于加密货币的价格、市值、交易量、交易时间等信息。 探索加密货币市场的趋势和模式,例如价格的波动情况、不同加密货币之间的相关性等。 数据处理与操作: 可能使用 Python 语言(Kaggle 上常用的数据分析语言),并运用一些数据处理和分析的库,如 pandas 用于数据的读取、清洗、整理和转换操作,将原始的加密货币数据转换为更易于分析的格式。 可视化展示: 通过可视化工具,如 matplotlib 或 seaborn 库,将加密货币的信息以图表的形式展示出来,以帮助直观地理解数据中的关系和趋势。 统计分析或预测: 可能会进行一些基本的统计分析,如计算加密货币价格的均值、中位数、标准差等统计量,以描述数据的特征。 或者使用机器学习或时间序列分析的方法对加密货币的价格进行预测,根据历史数据预测未来价格走势。 例如,使用 scikit-learn 进行简单的回归分析: 数据挖掘与特征提取: 挖掘加密货币数据中的特征,如找出影响价格的关键因素,对数据中的特征进行筛选和提取,以帮助更好地理解加密货币的市场行为。

    面对程序设计GJava

    类和对象、继承、封装、多态、接口、异常

    TF_demo1_keras.ipynb

    gee python相关教程

    夜间灯光数据 2023年全球_中国夜间灯光数据合集(数据权威)

    夜间灯光强度(平均灯光强度)的高低反映了一个地区城市化发展的水平,平均灯光强度越高,说明该地区城市群越多,城市化程度越高。夜间灯光数据现在越来越广泛地应用于经济增长分析、经济地理、城市经济学、数字经济等众多领域。 本数据包括三套: [1]中国类DMSP-OLS灯光数据1992-202 [2]中国超长序列灯光数据1984-2020 [3]全球类NPP-VIIRS夜间灯光数据2000-2022 包括:全国各省、市、县夜间灯光数据 矫正后夜间灯光数据 细分:标准差、平均值、总值、最大值和最小值

    工程项目总监绩效考核表.xls

    工程项目总监绩效考核表

    (数据权威)各省份一般公共预算转移支付数据(附送地级市转移支付)

    首先解释一下什么叫转移支付。其实,这和养老金的中央调剂是一样的。 每年,地方都要向中央缴纳财政。而中央又要根据各地方的财政实力,给予转移支付。比如一些经济弱省,本身财政收入就不够支出的,还得上交一部分给中央,怎么维持财政运转?由于各省市直接的财政收入能力存在差异,中央为实现各个地方的公共服务水平平等,于是便有了财政转移支付制度。 简单理解就是富省养穷省。 2022年全国一般预算内财政收入203703亿元,给地方转移支付了97144.75亿元,转移支付数额创下新高。

    基于门控卷积和堆叠自注意力的离线手写汉字识别算法研究.pdf

    基于门控卷积和堆叠自注意力的离线手写汉字识别算法研究.pdf

    逐月中国工业用水空间分布数据集(数据权威)

    【数据介绍】   作为第二大人类部门用水,高质量的工业用水格网数据对于水资源研究和管理至关重要。中国工业用水格网数据(China Industrial Water Withdrawal dataset, CIWW)基于超过 40 万家企业数据、月度工业产品产量数据和连续工业用水统计数据制作得到的一套1965-2020年逐月中国工业用水数据集,其空间分辨率为 0.1°和 0.25°。数据集包括工业用水、企业数量和企业生产总值(辅助数据)等变量,可被用于水文、地理学、环境、可持续发展等方面科学研究。 【数据来源】   数据来源为《中国经济普查年鉴》(省级工业取水量、工业产出)、《中国工业企业数据库》(企业地理位置、产值)、《中国工业产品产量数据库》(工业产品月生产量),以及《中国水资源公报》和(Zhou et al, 2020, PNAS)的工业用水量数据。 【数据处理】 首先通过2008年企业分布数据、经济普查年鉴中分省分部门的工业用水量和工业产值计算得到分省分部门工业用水效率和工业产品产量数据,得到了2008年逐月工业用水数据。然后结合中国水资源公报和相关文献中省级工业用水数据,以2008年工业用水的时空格局作为基础分配工业用水数据,最终得到1965-2020年逐月工业用水的格网数据。详细方法见High-resolution mapping of monthly industrial water withdrawal in China from 1965 to 2020 (Hou et al, 2024, ESSD). 将数据集与统计数据记录和其他数据集进行了验证,结果表示在时间尺度和空间尺度上都与统计数据具有一致性,相比已有工业用水数据有更好的精度。

    65 -质量管理部经理绩效考核表1.xlsx

    65 -质量管理部经理绩效考核表1

    11 -电脑部经理绩效考核表1.xlsx

    11 -电脑部经理绩效考核表1

    大英赛写作必备:实用英语万能句及其应用技巧

    内容概要:本文提供了针对大学生英语竞赛写作准备的重要资源——一系列通用的英文句子模板。这些模板涵盖了现代经济社会的各种话题,从科技进步到环境保护,以及个人品质和社会责任等,并且适用于论述类文章、观点对比和个人见解的表达。文章通过对每一句话的应用环境解释和语法提示,确保使用者可以在实际写作中正确且有效地应用这些表达方式。 适合人群:正在准备参加大学生英语竞赛的学生及其他希望提高书面表达能力的学习者。 使用场景及目标:考生能够在竞赛时间内迅速构建思路完整的文章,增强语言表达的流利性和规范性;帮助学习者积累高级词汇,提升英语写作水平并培养良好的思维逻辑。 阅读建议:结合历年优秀范文进行深入学习,熟悉不同类型话题下的表述方法;练习将提供的句子融入自身创作的文章中,通过不断修订和完善来巩固记忆。同时也可以用于日常的英语写作训练当中。

    法律事务专员绩效考核表.xls

    法律事务专员绩效考核表

    apache-commons-digester-javadoc-1.8.1-19.el7.x64-86.rpm.tar.gz

    1、文件内容:apache-commons-digester-javadoc-1.8.1-19.el7.rpm以及相关依赖 2、文件形式:tar.gz压缩包 3、安装指令: #Step1、解压 tar -zxvf /mnt/data/output/apache-commons-digester-javadoc-1.8.1-19.el7.tar.gz #Step2、进入解压后的目录,执行安装 sudo rpm -ivh *.rpm 4、安装指导:私信博主,全程指导安装

    永磁同步电机磁场定向控制(矢量控制)Simulink仿真模型波形展现与解析,永磁同步电机的磁场定向控制(矢量控制)simulink仿真模型,波形完美 ,核心关键词:永磁同步电机; 磁场定向控制(矢量控

    永磁同步电机磁场定向控制(矢量控制)Simulink仿真模型波形展现与解析,永磁同步电机的磁场定向控制(矢量控制)simulink仿真模型,波形完美 ,核心关键词:永磁同步电机; 磁场定向控制(矢量控制); Simulink仿真模型; 波形完美;,永磁同步电机矢量控制仿真模型:磁场完美调控,波形精确无误

    07 -储运部经理绩效考核表1.xlsx

    07 -储运部经理绩效考核表1

    OQC检验员(成品出货检验员)绩效考核表.xls

    OQC检验员(成品出货检验员)绩效考核表

    基于Matlab2020b的电机控制算法:无传感FOC算法Simulink仿真模型及实践指导,定位+电流闭环强拖+ 角度渐变切+ 速度电流双闭环+ 无传感器角度估算SMO+ PLL 控制方式 Sim

    基于Matlab2020b的电机控制算法:无传感FOC算法Simulink仿真模型及实践指导,定位+电流闭环强拖+ 角度渐变切+ 速度电流双闭环+ 无传感器角度估算SMO+ PLL 控制方式 Simulink 仿真模型 (Matlab2020b版本)以及教授模型搭建 这是一种常用的无传感FOC电机控制算法,掌握这种算法的基本原理,并有仿真模型在手,就可以用它来指导实践中的程序调试,做到实际项目不盲目调试。 模型特点: 1. 所有模块都做到了模块化,各个模块分区清楚,结构清晰。 2. 所有电机和控制参数均在m文件中体现,变量注释清楚,随用随改。 3. 速度环和电流环PI参数均实现自动整定。 4. 模型采用标幺值系统。 5. 各状态切使用stateflow,模型结构清晰。 6.通用表贴和内嵌式电机。 ,定位;电流闭环强拖;角度渐变切换;速度电流双闭环;无传感器角度估算SMO;PLL控制方式;Simulink仿真模型;Matlab2020b版本建模;教授模型搭建;模块化设计;参数自动整定;标幺值系统;Stateflow应用;通用表贴和内嵌式电机。,基于Matlab 2020b的FOC电机

Global site tag (gtag.js) - Google Analytics