关于MappedByteBuffer

JDK1.4中加入了一个新的包:NIO(java.nio.*).这个库最大的功能就是增加了对异步套接字的支持异步套接字对服务器程序来说更具吸引力.一般同步SOCKET服务器的实现都是采用线程池来处理客户请求的,基于请求超时时间和并发线程数目的限制,如果并发处理能力能够达到上千就已经是不错了.异步服务器的能力则至少是它的数倍(有人测试一个简单的ECHO服务程序,说可以达到上万个并发,不知道是否真的能达到). SocketChannel的读写是通过一个类叫ByteBuffer(java.nio.ByteBuffer)来操作的.这个类本身的设计是不错的,比直接操作byte[]方便多了. ByteBuffer有两种模式:直接/间接.间接模式最典型(也只有这么一种)的就是HeapByteBuffer,即操作堆内存(byte[]).但是内存毕竟有限,如果我要发送一个1G的文件怎么办?不可能真的去分配1G的内存.这时就必须使用"直接"模式,即MappedByteBuffer,文件映射. MappedByteBuffer也是类似的,你可以把整个文件(不管文件有多大)看成是一个ByteBuffer.这是一个很好的设计,除了一点,令人头疼的一点. MappedByteBuffer只能通过调用FileChannel的map()取得,再没有其他方式.但是令人奇怪的是,SUN提供了map()却没有提供unmap().这样会导致什么后果呢? 举个例子,文件test.tmp是一个临时构建的文件,在业务处理(通过SocketChannel发送)完之后将不再有效.一般的做法都是这样的: (1)File file = new File("test.tmp"); FileInputStream in = new FileInputStream(file); FileChannel ch = in.getChannel(); MappedByteBuffer buf = ch.map(FileChannel.MapMode.READ_ONLY, 0, file.length()); (2)SocketChannel sch = 已经构造好了; while (buf.hasRemaining()) sch.write(buf); (3)ch.close(); in.close(); file.delete(); 上面的操作都会正常的完成,除了最后一步:文件无法删除!即使你通过资源管理器直接强制删除也不行,说"文件正在使用". 为什么会出现这种情况? 说"文件正在使用",说明文件句柄没有清零,还有在使用它的地方---就是被MappedByteBuffer占用了!尽管FileChannel,FileInputStream都已经关闭了,但是在map里还打开着一个文件句柄.但是在外部看不见也无法操作它.那么这个句柄在什么时候才会正常地关闭呢?根据JAVADOC的说明,是在垃圾收集的时候.而众所周知垃圾收集是程序根本无法控制的. 既然MappedByteBuffer是从FileChannel中map()出来的,为什么它又不提供unmap()呢?SUN自己也没有讲清楚为什么.O'Reilly的<>中说是因为"安全"的原因,但是到底unmap()会怎么不安全,作者也没有讲清楚. 在SUN的BUG库中,这个问题在02年就有人提交了BUG报告,但是SUN自己不认为是BUG,而只是一个RFE(Request For Enhancement),有待改进. 好在网上牛人多.在BUG报告(http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4724038)中,有网友提出了一个解决的办法(具体参看上面的URL),可行,至少我在WINDOWS2000下测试是可以的.唯一的不足是并不是每次都能马上生效(文件彻底被删除),有的时候要延迟一会再试. 再抱怨两句.对于网友们的BUG报告,SUN似乎不怎么重视.粗看一下上面的BUG报告,会发现居然上世纪90年代的报告还赫然在列.有兴趣的朋友不妨仔细研究研究. 还有一点忘了说了.ByteBuffer是无法派生的.因为这个抽象类中定义了几个包抽象方法,即实现类只能位于java.nio包中.本来自己实现MappedByteBuffer也不难,只是效率比SUN实现的肯定要低一些.毕竟后者是可以直接与操作系统打交道的.而要是自己实现的化,只能通过一个中间的堆缓冲区进行过渡. 补充回答: 复制内容到剪贴板代码:
import java.nio.*;
import java.nio.channel.*;
import java.io.*;
public static void copy(File source, File dest) throws IOException {
FileChannel in = null, out = null;
try {
in = new FileInputStream(source).getChannel();
out = new FileOutputStream(dest).getChannel();

long size = in.size();
MappedByteBuffer buf = in.map(FileChannel.MapMode.READ_ONLY, 0, size);

out.write(buf);
if (in != null) in.close();
if (out != null) out.close();
}
}
转http://wenwen.soso.com/z/q132821597.htm

你可能感兴趣的:(java)