Java多线程读取单个大文件

时间:2009-10-10 06:28:17

标签: java concurrency io

Java多线程应用程序的有效方法是什么,其中许多线程必须读取完全相同的文件(大小> 1GB)并将其作为输入流公开?我注意到如果有很多线程(> 32),系统会开始争用I / O并且有大量的I / O等待。

我考虑将文件加载到一个由所有线程共享的字节数组中 - 每个线程都会创建一个ByteArrayInputStream,但是分配一个1GB的字节数组将无法正常工作。

我还考虑过使用单个FileChannel,每个线程使用Channels.newInputStream()在其上创建一个InputStream,但似乎FileChannel维护了InputStream的状态。

4 个答案:

答案 0 :(得分:10)

在我看来,如果你想避免IO争用,你将 将文件加载到内存中。操作系统会做一些缓冲,但是如果你发现这还不够,你将不得不自己动手。

你真的需要32个线程吗?大概你没有那么多的内核 - 所以使用更少的线程,你将获得更少的上下文切换等。

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

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

答案 1 :(得分:5)

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

答案 2 :(得分:1)

一些想法:

  1. 编写一个自定义的InputStream实现,作为FileChannel的视图。写这样,它不依赖于FileChannel中的任何状态。 (即:每个实例应该跟踪它自己的位置,读取应该使用底层FileChannel上的绝对读取。)这至少可以解决你使用Channels.newInputStream()的麻烦,但它可能无法解决你的IO争用问题

  2. 编写一个自定义的InputStream实现,作为MappedByteBuffer的视图。内存映射不应该像实际上一次将整个内容读入内存一样糟糕,但你仍然会占用1GB的虚拟地址空间。

  3. 与#1相同,但有某种共享缓存层。我不会尝试这个,除非1证明不够有效且2不可行。实际上,操作系统应该已经在#1中为你做了一些缓存,所以在这里你实际上是想要比操作系统文件系统缓存更聪明。

答案 3 :(得分:0)

这是一个非常大的文件。你能把文件作为一组较小的文件提供吗?即使在公司网络上,只需提供此文件也是一项重要工作。

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

您甚至可以更好地编写一些内容,将文件拆分为多个块并单独处理。