Ale*_*ler 24 java concurrency bytebuffer filechannel memory-mapped-files
我正在研究使用ByteBuffers从内存映射文件(通过FileChannel.map())以及内存中直接ByteBuffers 构建的东西.我试图了解并发和内存模型约束.
我已经阅读了FileChannel,ByteBuffer,MappedByteBuffer等所有相关的Javadoc(和源代码).很明显,特定的ByteBuffer(和相关的子类)有一堆字段,并且状态不受内存模型的保护观点看法.因此,如果跨线程使用该缓冲区,则必须在修改特定ByteBuffer的状态时进行同步.常见的技巧包括使用ThreadLocal包装ByteBuffer,复制(同步)以获取指向相同映射字节的新实例等.
鉴于这种情况:
B_all为整个文件提供了一个映射的字节缓冲区(比如它<2gb)B_1,该文件的一大块并将其提供给线程T1B_2指向相同映射字节的ByteBuffer 并将其提供给线程T2我的问题是:T1可以写入B_1,T2可以同时写入B_2并保证看到彼此的变化吗?T3可以使用B_all来读取这些字节并保证看到T1和T2的变化吗?
我知道,除非您使用force()指示操作系统将页面写入磁盘,否则不一定会在进程中看到映射文件中的写入.我不在乎.假设这个问题,这个JVM是编写单个映射文件的唯一进程.
注意: 我不是在寻找猜测(我可以自己做得很好).我想引用一些关于内存映射直接缓冲区保证(或不保证)的内容.或者,如果您有实际经验或负面测试用例,那么这也可以作为充分的证据.
更新:我已经完成了一些测试,让多个线程并行写入同一个文件,到目前为止,似乎这些写入可以立即从其他线程中看到.我不确定我是否可以依靠它.
小智 16
使用JVM的内存映射只是CreateFileMapping(Windows)或mmap(posix)的一个薄包装器.因此,您可以直接访问OS的缓冲区缓存.这意味着这些缓冲区是操作系统认为要包含的文件(操作系统最终会同步文件以反映这一点).
因此,无需调用force()来在进程之间进行同步.这些进程已经同步(通过操作系统 - 甚至读/写访问相同的页面).强制只在操作系统和驱动器控制器之间进行同步(驱动器控制器和物理盘片之间可能存在一些延迟,但是您没有硬件支持来执行任何操作).
无论如何,内存映射文件是线程和/或进程之间可接受的共享内存形式.这个共享内存与Windows中一个命名的虚拟内存块之间的唯一区别是最终与磁盘同步(事实上,mmap通过映射/ dev/null来完成没有文件内容的虚拟内存).
从多个进程/线程中读取写入内存仍然需要一些同步,因为处理器能够执行无序执行(不确定它与JVM交互的程度,但是你不能做出推测),而是写一个字节来自一个线程通常与写入堆中的任何字节具有相同的保证.一旦你写入它,每个线程和每个进程都将看到更新(即使通过打开/读取操作).
有关更多信息,请在posix中查找mmap(或者在Windows中使用CreateFileMapping,它的构建方式几乎相同.
不可以。JVM 内存模型 (JMM) 不保证多线程变异(未同步)数据会看到彼此的更改。
首先,鉴于访问共享内存的所有线程都在同一个 JVM 中,通过映射的 ByteBuffer 访问该内存这一事实无关紧要(通过 ByteBuffer 访问的内存没有隐式 volatile 或同步),所以问题相当于访问一个字节数组。
让我们重新表述这个问题,使其与字节数组有关:
- 经理有一个字节数组:
byte[] B_all- 创建对该数组的新引用:
byte[] B_1 = B_all,并提供给线程T1- 创建了对该数组的另一个引用:
byte[] B_2 = B_all,并将其提供给线程T2不要写入
B_1由线程T1得到见于B_2由丝T2?
不,如果T_1和之间没有一些显式同步,则不能保证看到此类写入T_2。问题的核心是 JVM 的 JIT、处理器和内存架构可以自由地重新排序一些内存访问(不仅仅是为了激怒你,而是通过缓存来提高性能)。所有这些层都希望软件明确(通过锁、易失性或其他显式提示)需要同步的地方,这意味着当没有提供此类提示时,这些层可以自由移动内容。
请注意,实际上您是否看到写入主要取决于硬件和不同级别缓存和寄存器中数据的对齐方式,以及正在运行的线程在内存层次结构中的“距离”有多远。
JSR-133 致力于精确定义 Java 5.0 左右的 Java 内存模型(据我所知,它在 2012 年仍然适用)。这就是您想要寻找明确(虽然密集)答案的地方:http : //www.cs.umd.edu/~pugh/java/memoryModel/jsr133.pdf(第 2 节最相关)。在 JMM 网页上可以找到更多可读的东西:http : //www.cs.umd.edu/~pugh/java/memoryModel/
我的部分答案是断言ByteBufferabyte[]在数据同步方面与 a没有区别。我找不到说明这一点的特定文档,但我建议java.nio.Buffer文档的“线程安全”部分会提及有关同步或 volatile 的内容(如果适用)。由于文档没有提到这一点,我们不应该期待这种行为。
我不认为这是有保证的。如果 Java 内存模型没有说它是有保证的,那么根据定义,它就是没有保证的。我会使用同步保护缓冲区写入或处理所有写入的一个线程的队列写入。后者与多核缓存配合得很好(最好为每个 RAM 位置配备 1 个写入器)。
| 归档时间: |
|
| 查看次数: |
7011 次 |
| 最近记录: |