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)不需要写入大小…