黑马程序员_O‘Reilly java nio学习笔记之通道_内存映射文件&&Channel-to-Channel 传输

---------------------- android培训java培训、期待与您交流! ----------------------


4.内存映射文件

    新的FileChannel类提供了一个名为map( )的方法,该方法可以在一个打开的文件和一个特殊

类型的 ByteBuffer之间建立一个虚拟内存映射。在FileChannel上调用map()方法会创建一个由磁盘文件支持的虚拟内存映射并在那块虚拟内存空间外部封装一个MappedByteBuffer对象。所有的 MappedByteBuffer对象都是直接的。

由 map()方法返回的MappedByteBuffer对象类似一个基于内存的缓冲区,只不过该对象的数据元素存储在磁盘上的一个文件中。调用get()方法会从磁盘文件中获取数据,此数据反映该文件的当前内容,即使在映射建立之后文件已经被一个外部进程做了修改。通过文件映射看到的数据同您用常规方法读取文件看到的内容是完全一样的。通过内存映射机制来访问一个文件会比使用常规方法读写高效得多,甚至比使用通道的效率都高。您应该注意到了没有unmap( )方法。也就是说,一个映射一旦建立之后将保持有效,直到MappedByteBuffer对象被施以垃圾收集动作为止。同锁不一样的是,映射缓冲区没有绑定到创建它们的通道上。关闭相关联的FileChannel不会破坏映射,只有丢弃缓冲区对象本身才会破坏该映射。

如何使用内存映射呢?

只有一种map()方法来创建一个文件映射,它的参数有 mode,position和 size。。我们可以创建一个MappedByteBuffer来代表一个文件中字节的某个子范围。例如,要映射100到 299位置的字节,可以使用下面的代码: 

buffer = fileChannel.map (FileChannel.MapMode.READ_ONLY, 100, 299); 

如果要映射整个文件则使用: 

buffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fileChannel.size()); 

与文件锁的范围机制不一样,映射文件的范围不应超过文件的实际大小。如果您请求一个超出文件大小的映射,文件会被增大以匹配映射的大小。假如您给size 参数传递的值是Integer.MAX_VALUE,文件大小的值会膨胀到超过2.1GB。即使您请求的是一个只读映射,map( )方法也会尝试这样做但是大多数情况下都会抛出一个IOException 异常。模式有以下几种定义:

         static FileChannel.MapMode PRIVATE     专用(写入时拷贝)映射模式。 

    static FileChannel.MapMode READ_ONLY   只读映射模式。 

static FileChannel.MapMode READ_WRITE  读取/写入映射模式。 

如果通道是以只读的权限打开的而您却请求MapMode.READ_WRITE模式,那么map( )方法会抛出一个NonWritableChannelException异常;如果您在一个没有读权限的通道上请求MapMode.READ_ONLY映射模式,那么将产生NonReadableChannelException异常。不过在以read/write 权限打开的通道上请求一个MapMode.READ_ONLY 映射却是允许的,此模式详解见书籍相应章节。如果映射是以MapMode.READ_ONLY或MAP_MODE.PRIVATE模式建立的,那么调用force( )方法将不起任何作用,因为永远不会有更改需要应用到磁盘上(但是这样做也是没有害处的)。 

MappedByteBuffer还定义了几个它独有的方法:

     MappedByteBuffer force() 

                将此缓冲区所做的内容更改强制写入包含映射文件的存储设备中。 

     boolean isLoaded()    判断此缓冲区的内容是否位于物理内存中。 

     MappedByteBuffer load()  将此缓冲区内容加载到物理内存中。此是一个代价高的操作。 

当用MappedByteBuffer对象来更新一个文件,您应该总是使用MappedByteBuffer.force( )而非FileChannel.force( ),因为通道对象可能不清楚通过映射缓冲区做出的文件的全部更改。

5. Channel-to-Channel 传输

有且只有FileChannel提供了通道与通道之间传输的方法:

abstract  long transferFrom(ReadableByteChannel src, long position, long count) 

          将字节从给定的可读取字节通道传输到此通道的文件中。 

abstract  long transferTo(long position, long count, WritableByteChannel target) 

      将字节从此通道的文件传输到给定的可写入字节通道。 

因此channel-to-channel传输中通道之一必须是FileChannel,不能在socket通道之间直接传输据,不过socket通道实现ritableByteChannel和 ReadableByteChannel接口,因此文件的内容可以用transferTo( )方法传输给一个socket通道,或者也可以用transferFrom( )方法将数据从一个socket通道直接读取到一个文件中。直接的通道传输不会更新与某个FileChannel关联的 position 值。

     对于传输数据来源是一个文件的transferTo()方法,如果position + count 的值大于文件

的 size值,传输会在文件尾的位置终止。假如传输的目的地是一个非阻塞模式的socket通道,那么

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

    Channel-to-channel传输是可以极其快速的,对于大量的数据传输,这会是一

个巨大的帮助。

以下程序赋值一个文件:

public class ChannelTransfer {

public static void main(String[] argv) throws Exception {

if (argv.length == 0) {

System.err.println("Usage: filename ...");

return;

}

catFiles(new FileOutputStream("copy.copy").getChannel(), argv);

}

private static void catFiles(WritableByteChannel target, String[] files)

throws Exception {

long sta=new Date().getTime();

for (int i = 0; i < files.length; i++) {

FileInputStream fis = new FileInputStream(files[i]);

FileChannel channel = fis.getChannel();

channel.transferTo(0, channel.size(), target);

channel.close();

fis.close();

}

System.out.println(new Date().getTime()-sta);

}

}

通过多次测试,复制一个大小为223M的文件用时2300毫秒左右,方差较小。而通过传统流的方式复制相同的文件,在相同条件下通过多次测试,复制一个大小为223M的文件用时不稳定,大概在2900~3000毫秒左右,偶尔往上出现较大波动,方差很大。




---------------------- android培训java培训、期待与您交流! ----------------------详细请查看:http://edu.csdn.net/heima

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值