单JVM中用synchronized保证两个MappedByteBuffer的putLong原子性可行吗?
问题解答
不能保证。原因如下:
MappedByteBuffer的写入本质:
putLong只是把数据写入JVM管理的内存映射区域(属于操作系统的页缓存),并不会立刻同步到磁盘。操作系统会根据自身的页置换策略异步刷盘——可能先刷buffer1对应的页,还没刷buffer2时,若JVM崩溃或OS异常,读应用就会看到buffer1的变更,buffer2还是旧值,直接破坏事务性要求。synchronized和force()的局限性:你用
synchronized只能保证单JVM内两个putLong的执行不会被打断,确保内存里的两个更新是顺序完成的,但管不了操作系统的刷盘顺序。就算你在同步块末尾调用buffer1.force()和buffer2.force()强制刷盘,这两个force()是独立操作,OS没有跨多个文件的原子刷盘机制——先刷完buffer1后如果出问题,buffer2的更新还是不会落地。
要实现跨JVM的事务性(要么全看到,要么全看不到),可以试试这些方案:
事务标记文件方案:
- 先写入
buffer1和buffer2的新数据 - 写入一个单独的标记文件(比如写入固定的完成标识,或者更新标记文件的版本号)
- 读应用先检查标记文件:只有标记有效(比如版本号更新完成),才读取两个数据文件的新值;否则读取旧值。如果写入过程中崩溃,标记文件没写完,读应用就不会感知到任何变更。
- 先写入
原子文件重命名方案:
- 把新数据写入两个临时文件(比如
file1.tmp、file2.tmp) - 写完后,分别将临时文件原子重命名为正式文件(比如
file1.dat、file2.dat)。注意:同一个文件系统下,操作系统的文件重命名操作是原子性的 - 读应用只读取正式文件,这样要么看到全部新文件,要么还是旧文件,不会出现部分更新的情况。
- 把新数据写入两个临时文件(比如
内容的提问来源于stack exchange,提问作者Abidi
相关产品推荐
相关产品推荐

