java 内存映射文件多线程读/写

java Memory mapped Files multithreading read / write

我有 2 个线程同时访问同一个大文件 (.txt)。

第一个线程正在读取文件。 第二个线程正在写入文件。

两个线程都访问同一个块,例如(开始:0,blocksize:10),但具有 不同的通道和缓冲区实例

Reader:

{
     int BLOCK_SIZE = 10;
     byte[] bytesArr = new byte[BLOCK_SIZE];
     File file = new File("/db.txt");
     RandomAccessFile randomFile = new RandomAccessFile(file, "r");
     FileChannel channel = randomFile.getChannel();
     MappedByteBuffer map = channel.map(FileChannel.MapMode.READ_ONLY, 0, BLOCK_SIZE);
     map.get(bytesArr , 0, BLOCK_SIZE);
     channel.close();
}

作者:

{
     int BLOCK_SIZE = 10;
     File file = new File("/db.txt");
     RandomAccessFile randomFile = new RandomAccessFile(file, "rw");
     FileChannel channel = randomFile.getChannel();
     MappedByteBuffer map = channel.map(FileChannel.MapMode.READ_WRITE, 0, BLOCK_SIZE);
     map.put(bytesToWrite);
     channel.close();
}

我知道如果两者同时启动,我会得到重叠异常!但我想知道的是,重叠到底发生在哪一点?我的意思是 "lock" 到底什么时候出现? 示例:假设作者首先获得访问权限,然后如果 reader 尝试访问,此时可能吗?:

 FileChannel channel = randomFile.getChannel();
 // 1- can reader access here?
 MappedByteBuffer map = channel.map(FileChannel.MapMode.READ_WRITE, 0, BLOCK_SIZE);
 // 2- can reader access here?
 map.put(bytesToWrite);
 // 3- can reader access here?
 channel.close();
 // 4- can reader access here?

1、2、3 或 4?

4是肯定的,因为频道被关闭了!其他点呢?

谢谢!

您不会从该代码或任何块中获得任何 锁定异常。文件锁在进程之间操作,而不是在线程之间操作。这里需要的是同步,或者信号量,或者 ReadWriteLocks。而且不需要使用两个通道。

我从与 OP 的聊天对话中总结了一些笔记。 OP 有这样一种心理模型(就像我们大多数人一样),即一旦线程写入数据结构,该数据结构立即对所有其他线程可见。在使用内存映射文件的 OP 测试中,他已经确认这在单个插槽 Intel CPU.

上似乎是正确的

不幸的是,这不是真的,Java 可以并且确实显示了硬件的底层行为。 Java 被设计为假设代码是单线程的,因此可以这样优化,直到被告知不是这样。这意味着什么会因硬件和热点版本(以及热点收集的统计数据)而异。这种复杂性,以及 运行 在单个插槽上的英特尔 CPU 使 OP 测试无效。

有关更多信息,以下链接将有助于更深入地了解 'Java Memory Model'。特别是 synchronized 不仅仅意味着 'mutual exclusion';在硬件方面,它也是关于 'data visibility' 和 'instruction ordering'。单线程代码想当然的两个话题。

如果这需要时间来消化,并且一开始您感到不知所措,请不要担心。一开始我们都有这种感觉。 Java 在隐藏这种复杂性方面做得非常出色,当且仅当您遵循这个简单的规则时。当线程读取或修改共享数据结构时,它必须在同步块内。即写线程和读线程。显然我正在简化,但遵循该规则,程序将始终有效。仅当您对 Java 内存模型、内存障碍以及它与不同硬件的关系有非常深刻的理解时才打破它(即便如此,并发专家甚至会尽可能避免打破该规则;单线程通常是简单得多,可以 surprisingly fast.. 由于这个原因,许多低延迟系统主要设计为单线程)。


直接回答OP问题。问题中的示例代码没有锁定。没有内存障碍,根本没有并发控制。因此,读写交互的行为是不确定的。它们可能有效,也可能无效。他们可能大部分时间都在工作。英特尔拥有所有 CPU 中最强的内存保证,并且 运行 单个插槽英特尔 CPU 上的测试用例会遗漏很多复杂的错误。在 Java 5 和 JSR 133 出来之前,Sun 也被这个问题抓住了(阅读关于为什么双重检查锁定在 Java 中被破坏的文章以获得更多详细信息)。