简体   繁体   English

如何理解JDK9 memory model?

[英]How to understand JDK9 memory model?

I'm learning the JDK9 memory model.我正在学习 JDK9 memory model。

After watching the speech Java Memory Model Unlearning Experience and reading the paper Using JDK 9 Memory Order Modes . After watching the speech Java Memory Model Unlearning Experience and reading the paper Using JDK 9 Memory Order Modes .

I'm confused about some concepts.我对一些概念感到困惑。

  1. Does opaque immediately guarantee the visibility ? opaque是否立即保证可见性

  2. How to understand partial order and total order in the paper?如何理解论文中的偏序全序

For the first question, the paper says对于第一个问题,论文说

It is almost never a good idea to use bare spins waiting for values of variables.使用裸旋转等待变量值几乎从来都不是一个好主意。 Use Thread.onSpinWait, Thread.yield, and/or blocking synchronization to better cope with the fact that "eventually" can be a long time, especially when there are more threads than cores on a system.使用 Thread.onSpinWait、Thread.yield 和/或阻塞同步来更好地应对“最终”可能需要很长时间这一事实,尤其是当系统上的线程数多于内核数时。

So if I write the code:所以如果我写代码:

// shared variable I and VarHandle I_HANDLE which referred to I
public static int I = 0;

public static final VarHandle I_HANDLE;

// Thread-1
I_HANDLE.setOpaque(1);

// Thread-2
while((int) I_HANDLE.getOpaque() == 0){
}

The thread-2 eventually terminate but maybe after a long time?线程 2 最终会终止,但可能会在很长一段时间后终止?

If so, is there any minimal approach to guarantee thread-2 immediately see the modifying by thread-1?如果是这样,是否有任何最小的方法来保证线程 2 立即看到线程 1 的修改? (Release/Acquire?volatile?) (发布/获取?易失性?)

There is no such thing like “immediately” for updates.没有像“立即”这样的更新。 Even electricity moves with a finite speed.甚至电也以有限的速度运动。 Generally, asking for a perceivable effect within a particular time span is like asking for a particular execution time for an operation.一般来说,在特定时间跨度内要求可感知的效果就像要求操作的特定执行时间一样。 Neither can be guaranteed, as they are properties of the underlying architecture which the JVM can't change.两者都不能保证,因为它们是 JVM 无法更改的基础架构的属性。

Practically, of course, JVM developers try to make the operations as fast as possible and all that matters to you, as a programmer, is that there is no faster alternative to opaque writes regarding inter-thread visibility of updates.实际上,当然,JVM 开发人员试图使操作尽可能快,作为程序员,对您来说最重要的是,对于更新的线程间可见性,没有比不透明写入更快的替代方案。 The stronger access modes do not change how fast an update will become visible, they add additional constraints to reordering of reads and writes.更强的访问模式不会改变更新变得可见的速度,它们会为读取和写入的重新排序添加额外的约束。

So in your example, the update will become visible as fast as the architecture and system load will allow 1 , but don't ask for actual numbers.因此,在您的示例中,只要架构和系统负载允许1 ,更新就会变得可见,但不要询问实际数字。 No-one can say how long it will take.没有人能说这需要多长时间。 If you need guarantees in terms of time quantities, you need a special (“real-time”) implementation that can give you additional guarantees beyond the Java Memory Model.如果您需要时间数量方面的保证,您需要一个特殊的(“实时”)实现,它可以为您提供除 Java Memory ZA559B87068921EEC05086CE5485E9748 之外的额外保证


1 To name a practical scenario: thread 1 and 2 may compete for the same CPU. 1举一个实际场景:线程 1 和 2 可能竞争同一个 CPU。 Thread 1 writes the value and continues to run for the operating system specific time before the task is switched (and it's not even guaranteed that thread 2 is the next one).线程 1 写入值并在任务切换之前继续运行操作系统特定时间(甚至不能保证线程 2 是下一个)。 This implies that a rather long time may elapse, in both terms, wall clock time and thread 1's progress after the write.这意味着写入后可能会经过相当长的时间,无论是挂钟时间还是线程 1 的进度。 Of course, other threads may also make a lot of progress on the other CPU cores in the meanwhile.当然,其他线程也可能同时在其他 CPU 内核上取得很大进展。 But it's also possible that thread 2's polling before thread 1 commits the write is the cause of thread 1 not getting a chance to write the new value.但也有可能线程 2 在线程 1 提交写入之前轮询是线程 1 没有机会写入新值的原因。 That's why you should mark such polling loops with onSpinWait or yield , to give the execution environment a chance to prevent such scenarios.这就是为什么您应该使用onSpinWaityield标记此类轮询循环,以使执行环境有机会防止此类情况发生。 See this Q&A for a discussion about the difference between the two.有关两者之间差异的讨论,请参阅此问答

In simple terms, opaque means that the read or write is going to happen.简单来说,不透明意味着读取或写入将要发生。 So it isn't optimized away by the compiler.所以它没有被编译器优化掉。

It doesn't provide any ordering guarantees with respect to other variables.它不提供关于其他变量的任何排序保证。

So it is good for example for performance counters, whereby 1 thread does an update and other threads read it.因此,它适用于性能计数器,其中 1 个线程进行更新,其他线程读取它。

But if you would do the following (pseudo)但是,如果您要执行以下操作(伪)

// global
final IntReference a = new IntReference();
final IntReference b = new IntReference();

void thread1(){
    a.setPlain(1);
    b.setOpaque(1);
}

void thread2(){
    int r1 = b.getOpaque();
    int r2 = a.getPlain();
    if(r1 == 1 && r2 == 0) println("violation");
}

Then it could be that 'violation' got printed because:然后可能是“违规”被打印出来,因为:

  • the stores of a,b get reordered a,b 的商店重新排序
  • the loads from a and b get reordered.来自 a 和 b 的负载被重新排序。

However if you would use an store release and load acquire, the reordering can't happen because the release and acquire provide ordering constraints with respect to other variables.但是,如果您使用存储发布和加载获取,则不会发生重新排序,因为发布和获取提供了相对于其他变量的排序约束。

void thread1(){
    a.setPlain(1);
    [StoreStore] <--
    [LoadStore]
    b.setRelease(1);
}

void thread2(){
    int r1 = b.getAcquire();
    [LoadLoad] <---
    [LoadStore]
    int r2 = a.getPlain();
    if(r1 == 1 && r2 == 0) println("violation");
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM