Java多线程读取单个大文件

7
在一个Java多线程应用程序中,许多线程必须读取完全相同的文件(大小> 1GB),并将其作为输入流公开,有没有一种有效的方法?我注意到,如果有许多线程(>32),系统开始争用I/O,并且有很多I/O等待。
我考虑过将文件加载到字节数组中,该数组由所有线程共享 - 每个线程将创建一个ByteArrayInputStream,但是分配一个1GB字节数组并不起作用。
我还考虑过使用单个FileChannel,每个线程使用Channels.newInputStream()在其上创建一个InputStream,但似乎是FileChannel维护了InputStream的状态。

1
每个线程是否需要整个文件的全部内容?还是可以通过搜索所需的相关数据来执行操作? - Tim Clemons
每个线程都需要读取整个文件。 - bob
该系统有8GB的内存,我不介意分配一个1GB的数组。但是JVM似乎并不喜欢这样做——它会尝试分配这个数组,并且会使用100%的CPU很长时间。 - bob
4个回答

10

如果想避免IO争用,看起来您需要将文件加载到内存中。操作系统会进行一些缓冲,但如果您发现这还不够,您就需要自己处理。

不过您真的需要32个线程吗?可能您没有那么多核心-使用较少的线程,您将获得更少的上下文切换等。

您的线程是否都从头到尾处理文件?如果是这样,您能否有效地将文件拆分成块?读取前10MB数据到内存中,让所有线程处理它,然后继续处理下一个10MB等。

如果以上方法对您没有用,您与文件大小相比有多少内存?如果您有足够的内存,但不想分配一个巨大的数组,您可以将整个文件读入内存,但分为许多单独的较小字节数组。然后,您必须编写跨越所有这些字节数组的输入流,但应该可行。


@jon,是否可以使用nio工具将Java结构映射到磁盘上的文件中,以便只需编写Java结构并让JVM / OS处理实际的读取细节? - Thorbjørn Ravn Andersen
1
@Thorbjorn:Java支持内存映射文件,但如果您拥有比操作系统更多关于如何使用文件的信息,则可能能够做得更好。 - Jon Skeet

5

您可以以只读模式多次打开文件。您可以以任何方式访问文件。只需将缓存留给操作系统。当速度太慢时,您可能需要考虑某种基于块的缓存,所有线程都可以访问相同的缓存。


1

一些想法:

  1. 编写一个自定义的InputStream实现,作为FileChannel的视图。编写此类时,不应依赖于FileChannel中的任何状态。 (即:每个实例都应保持自己的位置,并且读取应在底层FileChannel上使用绝对读取。)这至少可以解决Channels.newInputStream()的问题,但可能无法解决IO争用问题。

  2. 编写一个自定义的InputStream实现,作为MappedByteBuffer的视图。内存映射不应该比一次性将整个文件读入内存更糟糕,但您仍会占用1GB的虚拟地址空间。

  3. 与#1相同,但具有某种共享缓存层。除非1被证明效率不够且2不可行,否则我不会尝试此方法。 实际上,在#1中,操作系统应该已经为您执行了一些缓存,因此在这里,您实际上正在尝试比操作系统文件系统缓存更聪明。


0

这是一个非常大的文件。你能否将文件分成一些较小的文件集进行传输?即使在企业网络上,仅传输此文件也将是一项巨大的工作。

有时候更改流程比更改程序更容易。

你甚至可以编写一些代码来将文件拆分成多个块并分别处理它们。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接