3

我有 2 个线程同时访问同一个大文件(.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是肯定的,因为通道被关闭了!其他点呢?

谢谢!

4

2 回答 2

3

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

不幸的是,这不是真的,Java 可以并且确实展示了硬件的底层行为。Java 被设计为假定代码是单线程的,因此可以对其进行优化,直到另有说明为止。这意味着硬件和热点版本(以及热点收集的统计信息)会有所不同。这种复杂性以及在单插槽 Intel CPU 上运行使 OPs 测试无效。

有关更多信息,以下链接将有助于更深入地了解“Java 内存模型”。尤其是同步不仅仅意味着“相互排斥”;在硬件方面,它也与“数据可见性”和“指令排序”有关。单线程代码认为理所当然的两个主题。

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


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

于 2015-07-16T13:50:32.857 回答
1

您不会从此代码或任何块中获得任何锁定异常。文件锁在进程之间而不是线程之间运行。您需要的是同步、信号量或 ReadWriteLocks。并且没有必要使用两个通道。

于 2015-07-16T11:43:51.673 回答