如何在Java中实现缓冲/批处理文件通道?

如何在Java中实现缓冲/批处理文件通道?,java,io,real-time,nio,Java,Io,Real Time,Nio,这看起来并不简单,特别是对于读/写缓冲文件通道。是否有任何开源实现的地方,我可以根据我的实现 对于那些不理解的人来说,要清楚: FileChannel在操作系统级别执行缓冲,我想在Java级别执行缓冲。请阅读此处以了解: @Peter我想从一个快速的消息流将一个巨大的文件写入磁盘。缓冲和批处理是解决问题的方法。所以我想在Java中批处理,然后调用FileChannel.write。FileChannel只与ByteBuffers一起工作,所以它自然会被缓冲。如果您需要额外的缓冲来将数据从By

这看起来并不简单,特别是对于读/写缓冲文件通道。是否有任何开源实现的地方,我可以根据我的实现


对于那些不理解的人来说,要清楚:

FileChannel在操作系统级别执行缓冲,我想在Java级别执行缓冲。请阅读此处以了解:



@Peter我想从一个快速的消息流将一个巨大的文件写入磁盘。缓冲和批处理是解决问题的方法。所以我想在Java中批处理,然后调用FileChannel.write。

FileChannel只与ByteBuffers一起工作,所以它自然会被缓冲。如果您需要额外的缓冲来将数据从ByteBuffer复制到ByteBuffer,但我不确定您为什么要这样做

FileChannel不在操作系统级别进行缓冲

FileChannel确实告诉操作系统要做什么。操作系统通常具有磁盘缓存,但FileChannel不知道是否存在这种情况

我想在Java级别进行缓冲


你很幸运,因为你别无选择这是唯一的选项。

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

我建议使用
缓冲输出流
包装
文件输出流
。我不相信通过使用
ByteBuffer
FileChannel
,您会看到任何性能改进,如果您这样做的话,将留下大量难以维护的代码

理由很简单:无论您采取何种方法,所涉及的步骤都是相同的:

  • 生成字节。你没有说你打算怎么做,这可能会在等式中引入额外级别的临时缓冲。但无论如何,Java数据必须转换成字节
  • 将字节累积到缓冲区中。您希望在写入数据之前对其进行缓冲,这样就不会进行大量的小写入。这是必然的。但缓冲区在哪里并不重要
  • 跨JNI屏障将字节从Java堆移动到C堆。编写文件是一个本机操作,它不会直接从Java堆读取。因此,无论您是在Java堆上进行缓冲,然后移动缓冲的字节,还是在直接
    ByteBuffer
    中进行缓冲(是的,您需要直接缓冲),您仍然在移动字节。您将使用
    ByteBuffer
    进行更多的JNI调用,但这是一个边际成本
  • Invoke
    fwrite
    ,一种内核调用,它将字节从C堆复制到内核维护的磁盘缓冲区中
  • 将内核缓冲区写入磁盘。这将超过所有其他步骤的总和,因为磁盘速度很慢
  • 可能会获得或损失几微秒,具体取决于您如何实施这些步骤,但您无法更改基本步骤

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


    一句话:我敢打赌,您实际上是IO绑定的,而且没有办法解决这一问题,因为您需要的是更好的硬件。

    不太清楚您想要的是什么,但是我现在能想到的最好的东西是
    RandomAccessFile
    类:@SimonAndréForsberg参考我在问题中的编辑…你是否证明了
    BufferedOutputStream
    包装
    FileOutputStream
    是不够的?当你这么做的时候,你有没有确认你实际上是CPU绑定的而不是IO绑定的?@parsifal没有,但我想使用FileChannel,因为我在处理ByteBuffers…什么是“快速消息流”?我想从一个快速消息流将一个大文件写入磁盘。缓冲和批处理是解决问题的方法。所以我想在Java中批处理,然后调用FileChannel.write。如果使用NIO,您没有任何其他选项,因此我不确定您在寻找什么。假设我必须批处理1000条消息。我不想调用FileChannel.write 1000次。我想用Java缓冲这个,然后他们只调用一次FileChannel.write。我想我想要一个批处理文件通道。这就是我要做的。如果您不需要定期写入磁盘,您可以使用64KB的单个字节缓冲区,当到达的消息溢出时,您可以将其写入磁盘并清除缓冲区。我有一个库
    Java编年史
    ,它只会在16MB到1GB的块中添加更多字节缓冲区映射的内存。它不使用强制或重新映射文件,只是不断添加到末尾。如果您有一个64位JVM,您可以通过这种方式写入超过8 TB的数据。FileOutputStream和FileChannel都是非阻塞的吗?我想是的。。。所以FileChannel的唯一优点是它可以直接使用ByteBuffer。它有我们不在乎的力。但正如人们之前提到的,FileChannel比FileOutputStream更快:我对所有这些帖子都持极大的保留态度。斯图·汤普森(Stu Thompson)就如何优化IO密集型应用程序给出了很多很好的建议,但他的基准测试结果可能(将)与您的应用程序关系不大。我想我忘记强调的一点是,我还需要在大文件的任意点上阅读。因此需要进行某种交换。@chrisapotek-我认为您最好描述我们应用程序的实际需求——所有这些需求——并征求实现建议。实际上,您只是给出了实现的一些细节,并询问如何添加您已经决定的另一个解决方案。@chrisapotek它们都不是非阻塞的。Java中唯一的非阻塞通道是那些exte