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



我有两个线程并发访问同一个大文件(.txt)。

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

两个线程访问同一个块,例如(start:0, blocksize:10),但使用不同的通道&

读者:

{
     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();
}

我知道如果两者同时开始,我将得到重叠异常!但我想知道的是,重叠到底发生在什么时候?我是说"锁"到底是什么时候发生的?例如:假设写入器先获得访问权,那么如果读取器尝试访问,在哪个点是可能的?:

 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号是肯定的,因为通道被关闭了!其他点呢?

谢谢!

我正在总结与OP的聊天对话中的一些注意事项。OP有一个心智模型(像我们大多数人一样),一旦线程写入数据结构,该数据结构立即对所有其他线程可见。在使用内存映射文件的OPs测试中,他已经确认这在单个插座Intel CPU上似乎是正确的。

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

进一步的信息,下面的链接将有助于获得更深入的了解"Java内存模型"。特别是同步并不仅仅意味着"相互排斥"在硬件方面,它还涉及"数据可见性"one_answers"指令排序"。单线程代码认为理所当然的两个主题。

    Java内存模型
  • 双检查锁定被打破
  • JSR 133

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


直接回答OPs问题。问题中的示例代码中没有锁。没有内存屏障,没有并发控制。因此,读取和写入将如何交互的行为是未定义的。它们可能有效,也可能无效。他们可能大部分时间都在工作。英特尔拥有所有CPU中最强的内存保证,在单个插槽上运行测试用例将错过许多复杂的错误。在Java 5和JSR 133发布之前,Sun也遇到了这个问题(更多细节请阅读关于为什么Java中的双重检查锁定被打破的文章)。

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

最新更新