如何在Java中实现对映射到内存的文件的并发读取?

如何在Java中实现对映射到内存的文件的并发读取?,java,file-io,concurrency,nio,Java,File Io,Concurrency,Nio,我有许多线程同时读取同一个文件(总共大约100万个),只有一个线程更新该文件。我想在内存中映射文件以减少文件I/O。如何在Java中实现这一点 我基本上考虑了以下两种方法: 用字节数组存储文件,并在每次创建ByteArrayInputStream时读取多线程读取的缓冲区 通过NIO获得一个文件通道,将通道同步到从MappedByteBuffer读取以进行多线程读取 我不确定这些方法是否有效。如果有更好的解决方案,请帮助给出一些提示。使用NIO,每个线程创建自己的映射并在自己的专用缓冲区中读取数据

我有许多线程同时读取同一个文件(总共大约100万个),只有一个线程更新该文件。我想在内存中映射文件以减少文件I/O。如何在Java中实现这一点

我基本上考虑了以下两种方法:

  • 用字节数组存储文件,并在每次创建ByteArrayInputStream时读取多线程读取的缓冲区
  • 通过NIO获得一个文件通道,将通道同步到从MappedByteBuffer读取以进行多线程读取

  • 我不确定这些方法是否有效。如果有更好的解决方案,请帮助给出一些提示。

    使用NIO,每个线程创建自己的映射并在自己的专用缓冲区中读取数据。保持私有缓冲区大小最佳。操作系统在其文件缓存中以页的形式读取文件,并将页读入专用缓冲区。如果相同的区域由多个线程读取,那么数据将从文件缓存中的相同页面读取,从而节省一些文件i/o周期。下面是一个小图表来说明这一点。希望这有助于更好地理解


    参考上图,下面是一些解释。文件的一个区域映射到内存。创建映射只是一个逻辑标记,表示要从文件的特定部分读取。创建映射后,映射区域就可以读取了。当您开始读取时,操作系统会将文件数据提取到文件缓存中的页面中。该区域可以映射到一个或多个页面。现在,您将页面读入自己的专用缓冲区(一次多个页面以进行优化)。其他一些线程可能正在读取与第一个线程相同的区域,因此它也会将相同的页面读取到其专用缓冲区中。请注意,这次读取是从文件缓存进行的,没有页面错误。处理完私有缓冲区后,您请求进一步阅读。请注意,您一次将映射的一部分读取到私有缓冲区中。您的文件可能是100MB,您可以将10MB的部分映射到内存;你可以有40KB的专用缓冲区,然后先从10MB中读取40KB。然后请求下一个40KB,以此类推。操作系统会检查您要读取的数据是否已被提取到缓存中。如果没有,则会发生页面错误,操作系统会将请求的数据提取到页面中。同样,如果多个线程请求读取同一区域,则可以共享此数据。您可以很好地使用文件缓存本身进行读取,而不是创建自己的专用缓冲区。但是,如果文件跨多个区域同时读取多次,则可能导致多个页面错误。因此,在这种情况下,最好有一个最佳大小的专用缓冲区。

    嗨,Vikas,你能详细解释一下吗?我对NIO很陌生。@Grace我放了一张图表来说明它是如何工作的。请参阅web上提供的内存映射文件io示例。感谢您更详细的解释。在我的情况下,我将有一个大约100万个文件,以及大约10000个客户端,他们都需要读取整个文件。所以我最多需要10000个线程,每个线程需要100M的私有缓冲区?我想知道总内存使用率是否太高?一次有10000个客户端不是一个好主意。将线程数限制为最佳值。如果可能,请使用线程池。缓冲区不需要大到100MB。它可以小到1个文件缓存页,即4KB,也可以是页面大小的倍数。整个想法是你一页一页地阅读文件。这是透明的,你不需要做任何明确的事情。您将获得一个大小等于映射大小的内存映射缓冲区。这是一种虚拟缓冲区。因此,您将继续以连续流的形式读取此虚拟缓冲区。JRE和底层操作系统负责将文件数据映射到虚拟缓冲区,然后您从该虚拟缓冲区将数据分块读取到您的专用缓冲区。如果每个人都在读取,而没有人在写入,则您根本不需要同步。@EJP将有一个线程要写入,所有其他线程都要读取,我现在更喜欢使用字节[]上的ReadWriteLock。谢谢,但是,恕我直言,你的问题没有提到作者。它完全改变了画面。我建议您将这一关键事实编辑到您的问题中。@EJP感谢您的善意提醒~