首页 > 解决方案 > 为什么'发生在之前; 关系叫这样吗?

问题描述

我理解这个概念的所有内容,除了为什么这样称呼它。有人可以帮我理解吗?它仍然让我感到困惑..这是我唯一的问题。我读了几篇文章,我仍然无法弄清楚它的名字的动机。

标签: javamultithreadinghappens-before

解决方案


Reading this article will probably help.

The general gist is: The JMM is defined in terms of 'this limited set of events are defined to imply that one thing always happens before the other thing', which is where the term 'happens before' comes from. However, what that boils down to, is this jump in logic: "if the JMM says that A happens before B, what that actually means is that all code after B must be able to observe everything up to A.". Timing rules turn into observation rules.

But that observation rule is what you probably learned and how you understand the JMM, and that's good. However, I assume that 'if you add a synchronized block, it means other threads will observe your changes consistently, whereas if you do not, there is no guarantee that they would' doesn't seem related to the english words 'comes before'. But now you know.

A bit more in-depth

The VM wants to be able to re-order actions, both intra-thread and inter-thread, because that opens the door to optimizations. However, sometimes reordering would break the app. So how does the VM know not to re-order (how does the VM know that re-ordering would break it)? By NOT reordering 2 events if the VM realizes that there is a timing relationship between the 2 events - when the 2 events rely upon the fact that one should happen before the other. The JMM then breaks out which language constructs create such timing relationships, and demands that us java coders write our apps so that if we rely on a certain order, that we use one of these defined happens before relationships so that the VM knows and won't reorder on us.

It's 3 things:

  • Imperative: Within a single thread, all statements happen before all further statements - this is the obvious one. In: {x(); y();}, the VM assumes that the java code relies on the x() invoke happening before the y() invoke, whatever x and y are.

  • java.lang.Thread: calling .start() on a thread object happens-before that thread actually starting. If a thread .join()s another thread, all actions in the other thread happen-before that join() returns.

  • sync primitives - synchronized: If you hit the end of a synchronized() block on object FOO, code relies on the fact that this is fully completed before any other thread would then acquire the lock by starting a synchronized(FOO).

  • sync primitives - volatile: field writes happen before later volatile field reads.

So let's go back to what it really means, by way of that last one: It seems tautologous, no? That says: "A thing that happens before another thing, means the other thing happened later". That's like "Circles are round". But it goes to the intent of this stuff and what it really means:

It's not so much about actual execution times. It's about being able to witness the effects of it.

The volatile reads/writes thing is saying:

If thread A so happens to write to a volatile and B so happens to see that write, then that means anything else A did, volatile/synchronized or not, must also be visible to B then.

And thus we have moved from 'timing relationships' to 'visibility relationships', and that latter one is really how the JMM makes sense, and presumably how you understand it. Hopefully now you understand how we got to 'visibility' from 'timing' (and 'happens before' is obvious in light of 'it is about timing', presumably).

Here is an example:

class Example {
    public int field1 = 0;
    public int field2 = 0;

    public void runInA() {
        int f1 = field1, f2 = field2;
        field1 = 5;
        field2 = 10;
        System.out.println(f1 + " " + f2);
    }

    public void runInB() {
        int f1 = field1, f2 = field2;
        field1 = 20;
        field2 = 40;
        System.out.println(f1 + " " + f2);
    }
}

Here it would be acceptable for a VM to end up printing:

0 0
0 40

But that seems to make no sense! (Here thread B ran before A) - but somehow the second field write is visible, but the first one isn't? Huh? - but that's how it works, the JMM makes no guarantees.

Toss in a volatile write, though, and you can no longer observe this.


推荐阅读