7

我有许多线程同时读取同一个文件(大约 100M),并且只有一个线程来更新文件。我想将文件映射到内存中以减少 FILE I/O。如何在 Java 中做到这一点?

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

  1. 用字节数组存储文件,多线程读取时每次创建ByteArrayInputStream读取缓冲区。
  2. 用NIO获取一个文件通道,同步通道从MappedByteBuffer中读取,进行多线程读取。

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

4

1 回答 1

12

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

内存映射文件io

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

于 2012-05-14T09:42:18.987 回答