java - 多个线程可以看到 Java 中直接映射的 ByteBuffer 上的写入吗?

标签 java concurrency memory-mapped-files bytebuffer filechannel

我正在研究使用 ByteBuffers 的东西从内存映射文件(通过 FileChannel.map() )以及内存中的直接 ByteBuffers 构建。我正在尝试了解并发和内存模型约束。

我已经阅读了所有相关的 Javadoc(和源代码),例如 FileChannel、ByteBuffer、MappedByteBuffer 等。很明显,特定的 ByteBuffer(和相关子类)有一堆字段,并且状态不 protected 内存模型的观点。因此,如果跨线程使用该缓冲区,则在修改特定 ByteBuffer 的状态时必须进行同步。常见的技巧包括使用 ThreadLocal 来包装 ByteBuffer、复制(同步时)以获取指向相同映射字节的新实例等。

鉴于这种情况:

  1. manager 有一个映射字节缓冲区 B_all 用于整个文件(比如说它是 <2gb)
  2. manager 调用 B_all 上的 duplicate()、position()、limit() 和 slice() 来创建一个新的更小的 ByteBuffer B_1,该文件的一个 block 并将其提供给线程 T1
  3. manager 执行所有相同的操作来创建指向相同映射字节的 ByteBuffer B_2 并将其提供给线程 T2

我的问题是:T1 写 B_1 和 T2 写 B_2 可以同时保证看到对方的变化吗? T3 是否可以使用 B_all 读取这些字节并保证看到 T1 和 T2 的变化?

我知道,映射文件中的写入不一定会在进程中看到,除非您使用 force() 来指示操作系统将页面写入磁盘。我不在乎那个。对于这个问题,假设这个 JVM 是写单个映射文件的唯一进程。

注意:我不是在寻找猜测(我自己可以很好地猜测)。我想引用一些关于内存映射直接缓冲区保证(或不保证)的明确内容。或者,如果您有实际经验或负面测试用例,也可以作为充分的证据。

更新:我已经做了一些测试,让多个线程并行写入同一个文件,到目前为止,这些写入似乎可以立即从其他线程中看到。我不确定我是否可以依赖它。

最佳答案

与 JVM 的内存映射只是 CreateFileMapping (Windows) 或 mmap (posix) 的一个薄包装器。因此,您可以直接访问操作系统的缓冲区缓存。这意味着这些缓冲区是操作系统认为文件包含的内容(操作系统最终将同步文件以反射(reflect)这一点)。

因此不需要调用 force() 来在进程之间进行同步。进程已经同步(通过操作系统——甚至读/写访问相同的页面)。强制仅在操作系统和驱动 Controller 之间进行同步(驱动 Controller 和物理盘片之间可能存在一些延迟,但您没有硬件支持来做任何事情)。

无论如何,内存映射文件是线程和/或进程之间公认的共享内存形式。这个共享内存和 Windows 中的命名虚拟内存块之间的唯一区别是最终同步到磁盘(实际上 mmap 通过映射/dev/null 来实现没有文件的虚拟内存)。

从多个进程/线程读取写入内存仍然需要一些同步,因为处理器能够进行乱序执行(不确定这与 JVM 有多少交互,但你不能做出假设),但是写入来自一个线程的字节与正常写入堆中的任何字节具有相同的保证。写入后,每个线程和每个进程都会看到更新(甚至通过打开/读取操作)。

有关更多信息,请查看 posix 中的 mmap(或 Windows 的 CreateFileMapping,其构建方式几乎相同。

关于java - 多个线程可以看到 Java 中直接映射的 ByteBuffer 上的写入吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7002510/

相关文章:

c++ - 写入大于系统内存的内存映射区域?

java - ReportServer 中的 SMTP 配置和 java 错误。没有为 PKCS#12 KeyStore 提供密码

java - 如何在写入文件之前锁定java中的文件夹?

iphone - 如何在 [NSData dataWithContentsOfMappedFile :]? 后检查错误

c++ - 释放内存映射内存

java - 当值大于 x 时从 map 中删除的最佳方法是什么

java - 当我尝试 POST 请求时收到 400 响应

java - 在 HashMap 中存储多个类型对象并检索它们

java - 我们可以以编程方式更改语言语法吗?

java - 等待 SwingWorker 完成