`
hitqiang
  • 浏览: 36153 次
  • 性别: Icon_minigender_1
  • 来自: shangdpng
最近访客 更多访客>>
社区版块
存档分类
最新评论

如何提高java I/O的执行效率,MappedByteBuffer

阅读更多
如何提高java I/O的执行效率,MappedByteBuffer
复制内容到剪贴板代码:
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();
}
}谈谈MappedByteBuffer

JDK1.4中加入了一个新的包:NIO(java.nio.*).这个库最大的功能就是增加了对异步套接字的支持.
其实在其他语言中,包括在最原始的SOCKET实现(BSD SOCKET),这是一个早有的功能:异步回调读/写事件,通过选择器动态选择感兴趣的事件,等等.不过好在SUN终于也开始支持它了.我想这也是开放的好处之一吧(NIO是作为JSR-51项目引入的).

这里简单讲一下操作流程:

通过把一个套接字通道(SocketChannel)注册到一个选择器(Selector)中,不时调用后者的选择(select)方法就能返回满足的选择键(SelectionKey),键中包含了SOCKET事件信息.


异步套接字对服务器程序来说更具吸引力.一般同步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的<<Java NIO>>中说是因为"安全"的原因,但是到底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实现的肯定要低一些.毕竟后者是可以直接与操作系统打交道的.而要是自己实现的化,只能通过一个中间的堆缓冲区进行过渡.


我不知道为什么SUN不提供ByteBuffer的派生.毕竟这是一个很实用的类,如果允许派生,那么我就可以操作的就不仅仅限于堆内存和文件了,我可以扩展到任何存储设备.复制内容到剪贴板代码:

public boolean copyTo(String strSourceFileName, String strDestDir) {
File fileSource = new File(strSourceFileName);
File fileDest = new File(strDestDir);


// 如果源文件不存或源文件是文件夹
if (!fileSource.exists() || !fileSource.isFile()) {
System.out.println("错误: FileOperator.java copyTo函数,\n原因: 源文件["
+ strSourceFileName + "],不存在或是文件夹!");
return false;
}


// 如果目标文件夹不存在
if (!fileDest.isDirectory() || !fileDest.exists()) {
if (!fileDest.mkdirs()) {
System.out.println("错误: FileOperator.java copyTo函数,\n原因:目录文件夹不存,在创建目标文件夹时失败!");
return false;
}
}

try {
String strAbsFilename = strDestDir + File.separator + fileSource.getName();

FileInputStream fileInput = new FileInputStream(strSourceFileName);
FileOutputStream fileOutput = new FileOutputStream(strAbsFilename);

int i = 0;
int count = -1;

long nWriteSize = 0;
long nFileSize = fileSource.length();

byte[] data = new byte[BUFFER];

while (-1 != (count = fileInput.read(data, 0, BUFFER))) {
fileOutput.write(data, 0, count);
nWriteSize += count;
long size = (nWriteSize * 100) / nFileSize;
long t = nWriteSize;
String msg = null;
if (size <= 100 && size >= 0) {
msg = "\r拷贝文件进度: " + size + "% \t" + "\t 已拷贝: " + t;
} else if (size > 100) {
msg = "\r拷贝文件进度: " + 100 + "% \t" + "\t 已拷贝: " + t;
}
}

fileInput.close();
fileOutput.close();

System.out.println("\n拷贝文件成功!");
return true;

} catch (Exception e) {
System.out.println("异常信息:[");
e.printStackTrace();
return false;
}
}
http://www.loohost.com/redirect.php?tid=1967&goto=lastpost
分享到:
评论

相关推荐

    Java NIO 中英文版

    在传统的Java I/O中,使用的是Blocking I/O,即阻塞式I/O,这种模型下,线程在等待数据就绪时会被挂起,直到数据准备好才能继续执行,这在处理大量并发连接时效率较低。而Java NIO则引入了选择器(Selectors)和通道...

    The_Study_about_Java.nio.rar_java nio

    2. **非阻塞I/O(Non-blocking I/O)**:Java NIO引入了非阻塞模式,当数据未准备好时,不会阻塞,而是立即返回,允许程序执行其他任务。 三、Java NIO的主要特性 1. **多路复用**:通过选择器,一个线程可以同时...

    Scalable io in java.doc

    在这个主题中,我们主要关注Java的非阻塞I/O(Non-blocking I/O,通常缩写为NIO)框架。NIO自Java 1.4版本引入,与传统的阻塞I/O( Blocking I/O)模型相比,它提供了更高效的性能和扩展性。 标题"Scalable IO in ...

    JavaNIONIO概述Java开发Java经验技巧共4页

    而在NIO中,当没有数据可读或可写时,I/O操作不会立即阻塞,而是返回一个状态,让线程继续执行其他任务,从而提高了程序的效率。 5. **内存映射文件(Memory-Mapped Files)**: - NIO提供了一个叫做...

    java nio入门学习,两个pdf

    而NIO的核心理念在于非阻塞,它允许程序在没有数据可读或可写时继续执行其他任务,提高了程序的运行效率。 二、Java NIO核心组件 1. **通道(Channels)**:通道是数据传输的双向路径,可以用于读取和写入数据。Java...

    JavaNIO教程高清

    Java NIO(New I/O)是Java提供的一套新的输入/输出API,自Java 1.4版本起作为标准Java IO的替代方案被引入。NIO能够支持面向缓冲区的(Buffer-oriented)、基于通道的(Channel-based)I/O操作,与传统的基于流的I/...

    文件批处理模块_java_批处理_源码

    - **并发处理**:为了提高效率,可以使用多线程或Java 8的并行流来并发处理多个文件。`ExecutorService`和`Future`接口可以帮助管理和协调这些任务。 6. **异常处理**: 文件操作中常见的异常有`...

    Java nio源码

    总结,Java NIO是一个强大的I/O框架,通过非阻塞I/O和选择器,极大地提高了程序处理并发连接的效率。理解并熟练掌握NIO的使用,对于开发高并发、高性能的网络应用至关重要。通过分析NIO源码,我们可以深入了解其内部...

    NIO trick and trap .pdf

    **NIO**(New I/O)是Java为提高I/O操作效率而引入的新特性,最初在JDK 1.4版本中作为JSR 51规范的一部分被提出。随着技术的发展,在JDK 7中进一步扩展为NIO 2(JSR 203),引入了更多高级功能,如文件锁定、原子...

    Java_NIO深入浅出.doc

    传统的Java I/O模型主要依赖于阻塞式的流,这种模型在处理大量并发连接时效率较低,因为它会为每个连接创建一个单独的线程,而线程的创建和管理都消耗资源。 1. 传统IO模型: 在传统IO中,例如网络编程,服务器...

    nio.rar_Different_NIO_java nio package

    在非阻塞模式下,如果通道没有准备好读写,操作不会阻塞,而是立即返回,从而提高了程序的执行效率。 7. **管道(Pipe)**:管道提供了一个单向数据流的通道,用于在两个线程间传递数据,它不涉及实际的I/O设备。 ...

    Java NIO入门

    NIO的核心在于它能允许Java程序在处理输入和输出时不必等待操作完成,而是立即返回继续执行其他任务,从而极大地提高了程序的效率。 一、NIO的基本概念 1. **通道(Channels)**:通道是数据传输的双向通道,类似...

    NIO学习总结经典

    《NIO学习总结经典》这篇文章主要探讨了Java的New IO(NIO)框架,这是一个用于高效处理I/O操作的重要库,特别是在处理大量并发连接时。NIO与传统的IO(-blocking I/O)相比,提供了非阻塞的I/O模型,极大地提高了...

    Java NIO 中英文版 + Pro Java 7 NIO.2

    Java NIO,全称为Non-Blocking Input/Output(非阻塞输入/输出),是Java平台中用于高效处理I/O操作的重要框架。它在Java 1.4版本中被引入,替代了传统的IO模型,提供了更高级别的I/O操作机制,以适应并发编程的需求...

    ssd3exercise5

    4. **文件系统缓存**:理解Java的内存映射文件(MappedByteBuffer)和操作系统层面的页缓存,可以帮助减少磁盘I/O次数,提高SSD的效率。 5. **SSD寿命管理**:SSD有写入次数限制(称为PE或Program/Erase周期),...

    NIO学习笔记

    而NIO则引入了“通道(Channel)”和“缓冲区(Buffer)”的概念,使得数据读写可以非阻塞地进行,线程可以在等待I/O完成的同时处理其他任务,提高了系统效率。 NIO的核心组件包括: 1. **通道(Channel)**:类似...

    Java NIO系列教程

    Java NIO 通过引入 Channels、Buffers 和 Selectors 等组件,极大地提高了 I/O 操作的效率和灵活性。相比于传统的 Java IO API,Java NIO 支持更高效的非阻塞 I/O 模式,使得开发者能够更好地应对高并发场景下的数据...

Global site tag (gtag.js) - Google Analytics