如何在Java中实现缓冲/批处理的FileChannel?

时间:2012-09-25 16:23:52

标签: java io real-time nio

这看起来并不简单,特别是对于读/写缓冲的FileChannel。是否有任何开源实现的地方,我可以基于我的实现?


要清楚那些不理解的人:

FileChannel在操作系统级别进行缓存,我想在Java级别进行缓冲。请阅读此处以了解:FileChannel#force and buffering


@Peter我想从快速消息流中将大文件写入磁盘。缓冲和批处理是要走的路。所以我想在Java中批处理,然后调用FileChannel.write。

3 个答案:

答案 0 :(得分:3)

FileChannel仅适用于ByteBuffers,因此它是自然缓冲的。如果你需要额外的缓冲来将数据从ByteBuffer复制到ByteBuffer,但我不确定你为什么要这样做。

  

FileChannel在操作系统级别执行任务

FileChannel会告诉操作系统该做什么。操作系统通常有一个磁盘缓存,但FileChannel不知道是否是这种情况。

  

我想在Java级别进行缓冲

你很幸运,因为你没有选择。 ;)这是唯一的选择。

答案 1 :(得分:3)

我建议使用BufferedOutputStream包裹FileOutputStream。我不相信你会通过与ByteBufferFileChannel混淆看到任何的性能改进,并且如果你要留下很多难以维护的代码,你走那条路。

推理非常简单:无论采用何种方法,所涉及的步骤都是相同的:

  1. 生成字节。你没有说你打算如何做到这一点,它可以在等式中引入额外的临时缓冲水平。但无论如何,Java数据必须转换为字节。
  2. 将字节累积到缓冲区中。您希望在写入数据之前缓冲数据,这样您就不会进行大量的小写操作。这是给定的。但缓冲区的存在并不重要。
  3. 跨越JNI屏障将字节从Java堆移动到C堆。编写文件是本机操作,它不直接从Java堆读取。因此,无论您是在Java堆上缓冲,然后在直接ByteBuffer中移动缓冲的字节或缓冲区(是的,您需要直接缓冲区),您仍然在移动字节。您将使用ByteBuffer进行更多JNI调用,但这是一个边际成本。
  4. 调用fwrite,一个内核调用,将C堆中的字节复制到内核维护的磁盘缓冲区中。
  5. 将内核缓冲区写入磁盘。这将超过所有其他组合步骤,因为磁盘很慢
  6. 根据您实现这些步骤的确切方式,可能会有几微秒的增加或丢失,但您无法更改基本步骤。

    FileChannel确实为您提供了调用force()的选项,以确保第5步实际发生。这可能实际上降低您的整体性能,因为在写入字节之前,基础fsync调用不会返回。如果你真的想这样做,你总是可以从底层流中获取频道。

    结论:我愿意打赌你实际上已经受到了IO限制,并且除了更好的硬件之外没有办法解决这个问题。

答案 2 :(得分:-1)

我有两个线程,生产者线程生成ByteBuffers并将它们附加到队列的尾部,消费者线程每次从队列的头部删除一些ByteBuffers,并调用fileChannel.write(ByteBuffer [])。 / p>