概述

文件通道总是阻塞式的,因此不能被置于非阻塞模式。现代操作系统都有复杂的缓存和预取机制,使得本地磁盘 I/O 操作延迟很少。网络文件系统一般而言延迟会多些,不过却也因该优化而受益。 面向流的 I/O 的非阻塞范例对于面向文件的操作并无多大意义,这是由文件 I/O 本质上的不同性质造成的。对于文件 I/O,最强大之处在于异步 I/O( asynchronous I/O),它允许一个进程可以从操作系统请求一个或多个 I/O 操作而不必等待这些操作的完成。发起请求的进程之后会收到它请求的 I/O 操作已完成的通知。

异步 I/O 是在JDK 1.7 才被加入的 java.nio.channels.AsynchronousFileChannel 。

FileChannel

FileChannel对象不能直接创建。一个FileChannel实例只能通过在一个打开的file对象( RandomAccessFile、 FileInputStream或 FileOutputStream)上调用getChannel( )方法
获取。调用getChannel( )方法会返回一个连接到相同文件的FileChannel对象且该FileChannel对象具有与file对象相同的访问权限,然后您就可以使用该通道对象来利用强大的FileChannel API了。

FileChannel 线程安全

FileChannel 是线程安全的类,支持多个线程同时并发访问,但不是所有的方法都能多线程同时并发访问,比如,文件大小,file postion 等,该方法要想获取正确的值,只能加锁访问。

FileChannel 类结构

public abstract class FileChannel extends AbstractInterruptibleChannelimplements SeekableByteChannel, GatheringByteChannel, ScatteringByteChannel {protected FileChannel() {}public static FileChannel open(Path path, OpenOption... options) throws IOException {}public abstract int read(ByteBuffer dst) throws IOException;public abstract int write(ByteBuffer src) throws IOException;public abstract long position() throws IOException;public abstract FileChannel position(long newPosition) throws IOException;public abstract long size() throws IOException;public abstract FileChannel truncate(long size) throws IOException;public abstract void force(boolean metaData) throws IOException;public abstract long transferTo(long position, long count, WritableByteChannel target) throws IOException;public abstract long transferFrom(ReadableByteChannel src, long position, long count) throws IOException;public abstract int read(ByteBuffer dst, long position) throws IOException;public abstract int write(ByteBuffer src, long position) throws IOException;public static class MapMode {public static final MapMode READ_ONLY = new MapMode("READ_ONLY");public static final MapMode READ_WRITE = new MapMode("READ_WRITE");public static final MapMode PRIVATE = new MapMode("PRIVATE");}public abstract MappedByteBuffer map(MapMode mode, long position, long size) throws IOException;public abstract FileLock lock(long position, long size, boolean shared) throws IOException;public final FileLock lock() throws IOException {}public abstract FileLock tryLock(long position, long size, boolean shared) throws IOException;public final FileLock tryLock() throws IOException {}}

open() 方法

public static FileChannel open(Path path, Set<? extends OpenOption> options,FileAttribute<?>... attrs) throws IOException {}
public static FileChannel open(Path path, OpenOption... options) throws IOException {}

position() 方法

每个 FileChannel 都有一个叫“file position”的概念。这个 position 值决定文件中哪一处的数据接下来将被读或者写。

FileChannel 类为我们提供了两种position( )方法。

public abstract long position() throws IOException;
public abstract FileChannel position(long newPosition) throws IOException;
  • 第一种,不带参数的,返回当前文件的position值。返回值是一个长整型( long),表示文件中的当前字节位置
  • 第二种形式的 position( )方法带一个 long(长整型) 参数并将通道的 position 设置为指定值。

position 必须是大于等于0的整数。但是 postion 的大小可以超出文件的大小。

当 position 的位置大于文件的长度时分以下两种情况:

  1. 调用 read() 方法,无法读取的数据,相当于读取到文件末尾。
  2. 调用 write() 方法,会在当前position的位置写入缓冲区中的字节。写方法可能会引起产生文件空洞。

文件空洞

当磁盘上一个文件的分配空间小于它的文件大小时会出现“文件空洞”。对于内容稀疏的文件,大多数现代文件系统只为实际写入的数据分配磁盘空间(更准确地说,只为那些写入数据的文件系统页分配空间)。假如数据被写入到文件中非连续的位置上,这将导致文件出现在逻辑上不包含数据的区域( 即“空洞”)。

例如:文件大小为 10 byte,现在把position 设置为100,然后调用 write 方法写入10个字节,现在的文件大小为 110 字节。而文件系统为了优化而磁盘中实际只占用了20字节,其它90个字节未分配空间。当真正写入的时候才分配磁盘空间。
是否产生文件空洞,取决与文件系统的实现。

truncate() 方法

当需要减少一个文件的 size 时, truncate( )方法会砍掉您所指定的新 size 值之外的所有数据。如果 truncate 的 size 大于文件的 size 该文件不会修改。如果truncate 的 size 小于或等于当前的文件 size 值,该文件会把 truncate 的 size 后面的数据删掉。如果postion的值大于 truncate size 的值,在 truncate 后会把 postion的值修改为 truncate size 的值。

    public static void main(String[] args) throws Exception {RandomAccessFile fis = new RandomAccessFile(new File("d:\\a.txt"),"rw");FileChannel fs =fis.getChannel();ByteBuffer bb = ByteBuffer.allocate(10);fs.position(100);fs.write(bb);fs.position(1000);System.out.println("原始size:"+fs.size() + "\tposition:"+fs.position());fs.truncate(200);System.out.println("truncate 200\tsize:"+fs.size()+ "\tposition:"+fs.position());fs.truncate(100);System.out.println("truncate 100\tsize:"+fs.size()+ "\tposition:"+fs.position());}
原始size:110  position:1000
truncate 200    size:110    position:200
truncate 100    size:100    position:100

force() 方法

该方法告诉 FileChannel,强制把所有修改的数据全部写如到磁盘上。
文件系统可能为了性能,把要修改的数据先写入缓存,等缓存写满后一块同步写入到磁盘中,使用缓存来提高文件的读写速度。

transferTo() 和 transferFrom() 方法

transferTo( )和 transferFrom( )方法允许将一个通道交叉连接到另一个通道,而不需要通过一个中间缓冲区来传递数据。只有 FileChannel 类有这两个方法,因此 channel-to-channel 传输中通道之一必须是 FileChannel。您不能在 socket 通道之间直接传输数据,不过 socket 通道实现WritableByteChannel 和 ReadableByteChannel 接口,因此文件的内容可以用 transferTo( )方法传输给一个 socket 通道,或者也可以用 transferFrom( )方法将数据从一个 socket 通道直接读取到一个文件中。

直接的通道传输不会更新与某个 FileChannel 关联的 position 值。请求的数据传输将从position 参数指定的位置开始,传输的字节数不超过 count 参数的值。实际传输的字节数会由方法返回,可能少于您请求的字节数。

对于传输数据来源是一个文件的 transferTo( )方法,如果 position + count 的值大于文件
的 size 值,传输会在文件尾的位置终止。假如传输的目的地是一个非阻塞模式的 socket 通道,那么当发送队列( send queue) 满了之后传输就可能终止,并且如果输出队列( output queue)已满的话可能不会发送任何数据。类似地,对于 transferFrom( )方法:如果来源 src 是另外一个 FileChannel并且已经到达文件尾,那么传输将提早终止;如果来源 src 是一个非阻塞 socket 通道,只有当前处于队列中的数据才会被传输(可能没有数据)。由于网络数据传输的非确定性,阻塞模式的socket 也可能会执行部分传输,这取决于操作系统。许多通道实现都是提供它们当前队列中已有的数据而不是等待您请求的全部数据都准备好。

注意:
NIO,非阻塞通道,不要使用 transferTo 或 tranferFrom 来传输数据,传输的数据可能会不完整。

map() 方法

map( )的方法,该方法可以在一个打开的文件和一个特殊类型的 ByteBuffer 之间建立一个虚拟内存映射。在 FileChannel 上调用 map( )方法会创建一个由磁盘文件支持的虚拟内存映射( virtual memory mapping)并在那块虚拟内存空间外部封装一个 MappedByteBuffer 对象。

由 map( )方法返回的 MappedByteBuffer 对象(直接内存)的行为在多数方面类似一个基于内存的缓冲区,只不过该对象的数据元素存储在磁盘上的一个文件中。调用 get( )方法会从磁盘文件中获取数据。通过文件映射看到的数据同您用常规方法读取文件看到的内容是完全一样的。相似地,对映射的缓冲区实现一个 put( )会更新磁盘上的那个文件,并且您做的修改对于该文件的其他阅读者也是可见的。

通过内存映射机制来访问一个文件会比使用常规方法读写高效得多,甚至比使用通道的效率都高。因为不需要做明确的系统调用,那会很消耗时间。更重要的是,操作系统的虚拟内存可以自动缓存内存页( memory page)。这些页是用系统内存来缓存的,所以不会消耗 Java 虚拟机内存堆( memory heap)。

lock() 方法

调用带参数的 Lock( )方法会指定文件内部锁定区域的开始 position 以及锁定区域的 size。第三个参数 shared 表示您想获取的锁是共享的(参数值为 true)还是独占的(参数值为 false)。要获得一个共享锁,您必须先以只读权限打开文件,而请求独占锁时则需要写权限。另外,您提供的 position和 size 参数的值不能是负数。

FileChannel 的 lock 支持获取共享锁和独占锁(lock 方法的第三个参赛 shared)。是否支持共享锁还得依赖本地的操作系统实现。并非所有的操作系统和文件系统都支持共享文件锁。对于那些不支持的,对一个共享锁的请求会被自动提升为对独占锁的请求。这可以保证准确性却可能严重影响性能。

锁的对象是文件而不是通道或线程,如果在同一个进程使用多线程获取文件锁,只要一个能获取到锁,那么其它的所遇咸菜都可以获取到锁。

锁定区域的范围不一定要限制在文件的 size 值以内,锁可以扩展从而超出文件尾。因此,我们可以提前把待写入数据的区域锁定,我们也可以锁定一个不包含任何文件内容的区域,比如文件最后一个字节以外的区域。如果之后文件增长到达那块区域,那么您的文件锁就可以保护该区域的文件内容了。相反地,如果您锁定了文件的某一块区域,然后文件增长超出了那块区域,那么新增加的文件内容将不会受到您的文件锁的保护。

不带参数的 lock() 方法,默认获取的是独占锁,并且锁定的文件区域是 0 到 Long.MAX_VALUE。

public final FileLock lock() throws IOException {return lock(0L, Long.MAX_VALUE, false);
}

文件锁使用,详见文章:JAVA 文件锁 FileLock


想了解更多精彩内容请关注我的公众号

本人简书blog地址:http://www.jianshu.com/u/1f0067e24ff8    
点击这里快速进入简书
GIT地址:http://git.oschina.net/brucekankan/
点击这里快速进入GIT

NIO 之 FileChannel相关推荐

  1. Java Nio 之高级搬砖工(FileChannel)二

    Java Nio 系列 Java Nio 之Buffer Java Nio 之直接内存 Java Nio 之高级搬砖工(FileChannel) 一 Java Nio 之高级搬砖工(FileChann ...

  2. NIO FileChannel中的文件锁lock 学习笔记

    排它锁:又叫独占锁.对文件加排它锁后,该进程可以对此文件进行读写,该进程独 占此文件,其他进程不能读写此文件,直到该进程释放文件锁. 共享锁:某个进程对文件加共享锁,其他进程也可以访问此文件,但这些进 ...

  3. Java NIO系列教程(二) Channel

    为什么80%的码农都做不了架构师?>>>    Java NIO的通道类似流,但又有些不同: 既可以从通道中读取数据,又可以写数据到通道.但流的读写通常是单向的. 通道可以异步地读写 ...

  4. 万字长文:助你攻破 JAVA NIO 技术壁垒

    本文来源:https://honeypps.com/java/java-nio-quick-start/ 现在使用NIO的场景越来越多,很多网上的技术框架或多或少的使用NIO技术,譬如Tomcat,J ...

  5. Java NIO 之 ByteBuffer()

    2019独角兽企业重金招聘Python工程师标准>>> 名词解释 capacity : 容量,表示缓冲区中最大存储数据的容量.一旦声明不能改变. limit : 界限,表示缓冲区中可 ...

  6. java nio nio2 区别_Java NIO2:NIO概述

    一.概述 从JDK1.4开始,Java提供了一系列改进的输入/输出处理的新特性,被统称为NIO(即New I/O).新增了许多用于处理输入输出的类,这些类都被放在java.nio包及子包下,并且对原j ...

  7. 理解Java的NIO

    同步与阻塞 同步和异步是针对应用程序和内核的交互而言的. 同步:执行一个操作之后,进程触发IO操作并等待(阻塞)或者轮询的去查看IO的操作(非阻塞)是否完成,等待结果,然后才继续执行后续的操作. 异步 ...

  8. Java NIO之Channel(通道)

    **Java高级特性增强-NIO 本部分网络上有大量的资源可以参考,在这里做了部分整理并做了部分勘误,感谢前辈的付出,每节文章末尾有引用列表~ 写在所有文字的前面:作者在此特别推荐Google排名第一 ...

  9. 【学习】009 NIO编程

    NIO概述 什么是NIO? Java NIO(New IO)是一个可以替代标准Java IO API的IO API(从Java 1.4开始),Java NIO提供了与标准IO不同的IO工作方式. Ja ...

最新文章

  1. 2017年诺贝尔生理学或医学奖揭晓
  2. 创建linux目录,Linux目录的创建方法
  3. 全球及中国低温纳米定位器行业发展趋势分析与风险评估报告2021-2027年版
  4. javascript --- 从数组中,找出比给定元素大一丁点的元素
  5. 连接查询_左连接/右连接/全连接的区别
  6. form表单 vue 拖拽_vue实现可视化可拖放的自定义表单(代码示例)
  7. CCNP精粹系列之十三-----OSPF路由汇总
  8. Daily Scrum M2 11-5
  9. 什么是UPS电源系统
  10. Java学习笔记 06 数字格式化及数学运算
  11. 数据结构实验病毒感染检测问题(C++)
  12. 5.12 CopyNet和 Pointer-Generator Net 复制机制和指针-生成器网络
  13. Oracle 基本函数-数值、字符、 Instr()、日期、转换、SQL 操作符、trunc 截断
  14. java中事物的注解_JAVA中对事物的理解
  15. 故障排查 node_Kubernetes 故障解决心得(一)
  16. JAVA中interface接口的使用
  17. 注释一下blk_update_request函数
  18. 看我如何快速拿下整个C段主机权限
  19. 网络协议实验四 ARP 协议分析实验
  20. tensorflow2.0 基于LSTM模型的文本生成

热门文章

  1. 三十七、下篇 | tkinter实现一个翻译软件
  2. EM算法和GMM(中)
  3. 小白入门商业数据分析师的课程测评
  4. 悉尼大学 伦敦大学联合出品:知识蒸馏最新综述
  5. 微软开源项目NeuronBlocks - 像搭积木一样构建NLP深度学习模型
  6. 综述 | 知识图谱发展概述
  7. 03 | 高可用保证:Nacos 如何有效构建注册中心集群
  8. 开篇词丨这样学Redis,才能技高一筹
  9. SpringMVC获取请求参数-POJO类型参数
  10. JAVA——Scanner读取文件