您认为在Java中实现发布/获取对的获取部分的最佳方法是什么?
我尝试使用经典发布/获取语义(没有StoreLoad
并且没有跨线程的顺序一致性)来模拟我的应用程序中的一些操作。
有几种方法可以在JDK中实现商店版本的粗略等效。 java.util.concurrent.Atomic*.lazySet()
和基础sun.misc.Unsafe.putOrdered*()
是最常被提及的方法。但是,没有明显的方法来实现负载获取。
允许lazySet()
内部大多使用volatile
变量的JDK API,因此它们的商店版本与易失性加载配对。理论上,易失性负载应该比负载获取更昂贵,并且在前面的存储释放的上下文中不应该提供任何纯粹的负载获取。
sun.misc.Unsafe
未提供getAcquire()*
个putOrdered*()
方法,即使为即将推出的VarHandles API计划了此类获取方法。
听起来像它可以工作的东西是一个普通的负载,然后是sun.misc.Unsafe.loadFence()
。我在其他任何地方都没有看到这种情况,这有点令人不安。这可能与它是一个相当丑陋的黑客有关。
P.S。我很清楚JMM没有涵盖这些机制,它们不足以维持顺序一致性,并且它们创建的动作不是同步动作(例如我理解它们例如打破了IRIW)。我也理解Atomic*/Unsafe
提供的商店版本通常用于急切地将引用或生产者/消费者场景中的空白作为一些重要索引的优化消息传递机制。
答案 0 :(得分:5)
易失性读取正是您所寻找的。 p>
实际上,相应的易失性操作已经具有释放/获取语义(否则发生在配对易失性写入读取之前是不可能的),但成对的易失性操作不仅应该是顺序一致的(〜之前发生),而且它们应该在total synchronization order中,这就是在volatile写入之后插入StoreLoad
屏障的原因:为了保证不同位置的易失性写入的总顺序,所以所有线程都会以相同的顺序看到这些值。
易失性读取从热点代码库获取语义:proof,在每次易失性读取后,Doug Lea也会在JSR-133 cookbook(LoadLoad
和LoadStore
障碍中直接推荐
Unsafe.loadFence()
也具有获取语义(proof),但不用于读取值(您可以对普通易失性读取执行相同操作),但是要防止使用后续易失性读取重新排序普通读取。这在StampedLock中用于乐观阅读(请参阅StampedLock#validate
方法实现和用法)。
在评论中讨论后更新。
让我们检查Unsafe#loadStore()
和volatile read是否相同并且具有获取语义。
我正在查看热点C1 compiler source code以避免阅读C2中的所有优化。 它将字节码(实际上不是字节码,但其解释器表示)转换为LIR(低级中间表示),然后将图转换为实际操作码取决于目标微体系结构。
Unsafe#loadFence
是intrinsic,其中包含_loadFence
别名。在C1 LIR generator中,它会生成以下内容:
case vmIntrinsics::_loadFence :
if (os::is_MP()) __ membar_acquire();
其中__
是生成LIR的宏。
现在让我们看看同一个LIR生成器中的volatile implementation。它尝试插入空检查,检查IRIW,检查我们是否在x32上并尝试读取64位值(使用SSE / FPU制作一些魔法),最后,引导我们使用相同的代码:
if (is_volatile && os::is_MP()) {
__ membar_acquire();
}
汇编程序生成器然后插入特定于平台的获取指令here。
查看具体实现(此处没有链接,但所有链接都可以在src / cpu / {$ cpu_model} / vm / c1_LIRAssembler _ {$ cpu_model} .cpp中找到)
SPARC
void LIR_Assembler::membar_acquire() {
// no-op on TSO
}
86
void LIR_Assembler::membar_acquire() {
// No x86 machines currently require load fences
}
Aarch64(弱存储模型,应该存在障碍)
void LIR_Assembler::membar_acquire() {
__ membar(Assembler::LoadLoad|Assembler::LoadStore);
}
根据无政府主义architecture description,这种membar将在加载后编译为dmb ishld
指令。
PowerPC(也是弱内存模型)
void LIR_Assembler::membar_acquire() {
__ acquire();
}
然后转换为特定的PowerPC指令lwsync
。根据{{3}} lwsync
在语义上等同于
lwsync订单商店|商店, 负载|店铺, 负载|负载, 但不是Store | Load
但只要PowerPC没有任何较弱的障碍,这就是在PowerPC上实现获取语义的唯一选择。
<强>结论强>
易失性读取和Unsafe#loadFence()
在内存排序方面是相同的(但可能不是在可能的编译器优化方面),在大多数流行的x86上它是无操作的,而PowerPC是唯一支持的架构,没有精确的获得障碍。
答案 1 :(得分:1)
根据您的具体要求,执行非易失性加载,可能后跟可能的易失性加载是Java中最好的。
您可以使用
的组合来完成此操作public static void readData (Scanner inputFile, double [][] payInfo, String [] names) {
Integer payInfoLenght = payInfo.lenght;
Integer namesLenght = names.lenght;
if (MAX_EMPLOY > payInfoLenght || MAX_EMPLOY > namesLenght) {
System.out.println("Wrong size of tabels");
} else {
for (int i = 0; i < MAX_EMPLOY; i++) {
if (inputFile.hasNextDouble()) {
payInfo [i][0] = inputFile.nextDouble();
}
if (inputFile.hasNextDouble()) {
payInfo [i][1] = inputFile.nextDouble();
}
if (inputFile.hasNextLine()) {
names [i] = inputFile.nextLine();
}
}
}
}
此模式可用于环形缓冲区,以最大限度地减少缓存行的流失。