JavaMMAP缓冲存储器模型

JavaMMAP缓冲存储器模型,java,mmap,java-memory-model,Java,Mmap,Java Memory Model,假设两个线程同时使用单个内存映射缓冲区(java.nio.channels.FileChannel.map()) 第一个线程是: 写入记录的大小 编写记录本身 重复一遍 比如说 ByteBuffer buffer = channel.map(READ_WRITE, 0, ...) buffer.putShort((short) data.length); buffer.put(data); 读取线程正在读取这些数据包: short size = buffer.getShort(); by

假设两个线程同时使用单个内存映射缓冲区(
java.nio.channels.FileChannel.map()

第一个线程是:

  • 写入记录的大小
  • 编写记录本身
  • 重复一遍
比如说

ByteBuffer buffer = channel.map(READ_WRITE, 0, ...)

buffer.putShort((short) data.length);
buffer.put(data);
读取线程正在读取这些数据包:

short size = buffer.getShort();
byte[] bytes = new byte[size];
buffer.get(bytes);
我不时地从buffer.get(bytes)中获取
BufferUnderflowException
,这是合理的,因为在这个幼稚的代码中显然存在竞争。读取可以在写入大小之后但在数据包被读取之前发生


问题是,写入mmap缓冲区是否易变?我可以安全地解决这个问题吗?只需在写入线程中重新排序写入操作,这样在使用数据包本身或某些外部协调机制之前,数据包的长度就永远不可见了?

这里没有与
volatile
相关的内容。你有两个需要原子化的写操作。我不知道重新排序写操作如何修复任何问题,volatile也不会使事情原子化

您需要确保在写入头和数据之前不能执行读取,因此需要进行外部同步


同样值得注意的是,
MappedByteBuffer
在很大程度上依赖于底层文件系统和操作系统,因此您最好确保您没有编写依赖于本机行为的脆弱代码。

我不认为我真的需要原子更新。在我看来,以前发生的事情应该足够了。数据包写入应该发生在数据包大小之前。这似乎是一个基本的误解。当您得到一个
BufferUnderflowException
时,它意味着剩余字节不足,这是
limit
position
的差异。由于映射字节缓冲区的大小从您调用
channel.map
的时间点起就固定不变,因此实际写入与此无关。除此之外,当您在两个线程中使用相同的缓冲区实例时,a)您必须在
put
get
之间调用
flip()
,b)必须确保此缓冲区的位置和限制没有争用,c)不需要写入大小…