3

The code is simple.

// not annotated with volatile
public static int I = 0;

public static int test(){
    I = 1;
    return I;
}

There is a thread that invokes the method test.

Is it possible the method test will return the value '0'?

In other words, the reading of a shared variable maybe not see the modifying by the same thread.


update

The question just very simple, but I make its obscurity, I'm really sorry about it.

The a thread means a single thread.

And the question is duplicated with it.

梁雨生
  • 385
  • 3
  • 16
  • 1
    I am voting to close this question as unclear. The question does not say whether whether one thread or two threads are involved. And the code is incomplete. Note that the answer is **very** different for different interpretations of this question. – Stephen C Jan 28 '21 at 03:35
  • A minimal reproducible example would help us to understand what you are actually asking. – Stephen C Jan 28 '21 at 03:40
  • @StephenC You are right, that's my mistake that not clarify the question, I'm really sorry about that, my question is derived from another code, so I'm trying to simplify it, but that seems confusing us, I will reopen another question to talk about the original code. – 梁雨生 Jan 28 '21 at 03:56
  • It would be better if you clarified >>this<< question rather than opening another question. That is what the [EDIT button](https://stackoverflow.com/posts/65929942/edit) is for ... – Stephen C Jan 28 '21 at 04:19

3 Answers3

3

No, it will be 1 if no other thread is involved but the one that will invoke the method.

chrylis -cautiouslyoptimistic's answer is worth reading for an alternative scenario as well.

Two reasons:

  • I is just altered by its owner, and if the other thread just calls test(), there's no option for it to get a 0 as I's value.
  • The second thread won't read Class.I's value, but the result of the test() method. The assignation I=1 happens before the return so is guaranteed to offer the latest updated value (which has only been updated by the owner, once).
aran
  • 10,978
  • 5
  • 39
  • 69
  • 1
    Hmmm, I just followed OP's question: `a thread that invokes the method test`. Your scenario is right indeed, but wasn't included in the original question : ( – aran Jan 28 '21 at 02:07
  • Well, since `I` is public then it can be altered as @chrylis-cautiouslyoptimistic- states – Luiggi Mendoza Jan 28 '21 at 02:08
  • Well, updated my statement so it focuses on the example...oh guys you're tough – aran Jan 28 '21 at 02:10
  • 1
    @chrylis-cautiouslyoptimistic thanks for your answer, but the question is just very simple and my goal to ask this question is to discuss `Intra-thread coherence` as the title says, so let's back to the question and ignore other threads temporarily. – 梁雨生 Jan 28 '21 at 02:22
  • 1
    @梁雨生 Then you need to ask _a single question_, not two related questions that have different answers. – chrylis -cautiouslyoptimistic- Jan 28 '21 at 02:23
  • @aran @chrylis-cautiouslyoptimistic Well, what if I modify the `I=1` to `if(I == 0) {I=1}`, is the answer changed? – 梁雨生 Jan 28 '21 at 02:25
  • @梁雨生 no, with your original context, the result would be 1 – aran Jan 28 '21 at 02:26
  • @chrylis-cautiouslyoptimistic I'm sorry that my question may be a little misleading. – 梁雨生 Jan 28 '21 at 02:27
  • @aran Then, what if this code is under multithread? There are many threads that invoke the method `test`, does they see the same value '1'? – 梁雨生 Jan 28 '21 at 02:38
  • yes, they will all be given the value 1 after calling `test()`. The write is made from an unique thread, and the assignation happens-before any return. – aran Jan 28 '21 at 02:40
  • @aran Hum, now the code looks like that ```if(I == 0) {I=1} return I;```, there are two reads of shared variable I `first: I == 0` and `second: return I`, so is it possible that optimizer reorders the second reading before the first? And it will cause the result '0'. – 梁雨生 Jan 28 '21 at 03:03
  • @梁雨生 the only issue with that scenario would be that `I=1` would be executed twice; But the result would be the same in this context, which will be `I` being assigned 1 before being returned – aran Jan 28 '21 at 03:05
  • @梁雨生 here you have an example of such possible context regarding compiler opts https://stackoverflow.com/questions/16780937/tips-to-prevent-deadlocks-in-java/16781008#16781008 – aran Jan 28 '21 at 03:06
  • @aran Okay, I would learn more about JMM, thanks for your help. – 梁雨生 Jan 28 '21 at 03:10
  • I would strongly suggest you start reading the `JLS` and start understanding it. You have a rather weird, at best, understanding of how `JMM` works. I do not downvote, but, imo, you do deserve one here, sorry. – Eugene Jan 28 '21 at 03:21
  • "The assignation I=1 is enough for it to update the value" - no it is not. `JLS` does not offer ANY guarantees for plain stores; while typical x86 cpus will place that value in a `StoreBuffer`, and other threads will see that store much later, when it is drained. The return value from `test` is irrelevant, what is matters _only_ is that it does `I=1`, while other reader threads will read that `I`. Not only is a thread allowed to see `0`, but [reading it twice](https://stackoverflow.com/questions/64983578/reading-a-stale-value-after-a-newer-value-was-read) is allowed to see it as `1, 0`. – Eugene Jan 28 '21 at 03:28
  • @Eugene yes, but talking on the context; just one thread invoking `test()`, not accessing/reading the field directly. There's no way it will see 0, if the only operation done is calling `test()`. These other scenarios are not in the scope of the question thou – aran Jan 28 '21 at 03:32
  • what do you mean are not in scope? did you read and _understand_ the last sentence in the question? – Eugene Jan 28 '21 at 03:33
  • `There is a thread that invokes the method test. Is it possible the method test will return the value '0'?` that is the question to focus on, and that's the only scenario I was talking about. Where is it supposed other thread to access I directly? Should'nt be assumed the last sentence is referring to the thread that invokes `test()`? – aran Jan 28 '21 at 03:35
  • There's really a big difference between `Is it possible the method test will return the value '0'?` and `is it possible for a thread to see 0 as I's value when accessing it directly while other thread is calling test()`? – aran Jan 28 '21 at 03:37
  • I'm not saying I'm right here. I'm just saying that's the context I focused on, that's what I understood from OP's question. And yes, some of my assumptions are wrong and I assume it, that's why I try to learn something everyday. – aran Jan 28 '21 at 03:39
  • 1
    Even if you did understood it as "single thread" involved, a proper [JLS quotation](https://stackoverflow.com/a/65930536/1059372) is needed; otherwise you are making assumptions. Nothing wrong with learning, but these things are not trivial and neither should they be answered in a trivial way. – Eugene Jan 28 '21 at 03:43
  • @Eugene I sincerely agree with you there. You'd see my answers improving with time, I swear. – aran Jan 28 '21 at 03:44
  • another skill that SO will teach you is to see beyond the question. OP's question is far, far more complicated and interesting, but has a bad presentation. Just read the comments under my answer. – Eugene Jan 28 '21 at 04:22
  • @Eugene Just taking a look now. But you are incorrect here: *another skill that SO will teach*... Not SO, but users like you. Honest here. Thank you, – aran Jan 28 '21 at 04:26
3

Any answer that does not explain in terms on java language specification is only partially correct, if correct at all.

You need to make a clear distinction between actions that happens within a single thread and are tied together by program order and that in turn creates a happens-before connection, specifically via:

If x and y are actions of the same thread and x comes before y in program order, then hb(x, y).

That rule tells you that if you think about this code in single threaded world, it will always print 1.

And on the other hand, actions that create synchronizes with connections across different threads, and implicitly those create happens-before, via:

If an action x synchronizes-with a following action y, then we also have hb(x, y).

In your case, I is a plain field, as such every operation related to it is a plain store and/or a plain load. Such stores and loads do not created any connections at all according to the JLS. As such some thread that reads I can always read it as 0 if there is a writing thread involved.

Eugene
  • 117,005
  • 15
  • 201
  • 306
  • Maybe I should explain it, my question is derived from the `double-check` that [speech](https://www.youtube.com/watch?v=TK-7GCCDF_I) shows, ``` public T get () { if (val == null) {. // point 1 synchronized (this) { if (val == null) { val = new T(); } } } return val; /// point 2 } ``` Aleksey Shipilёv says the point 1 should be coherence with point 2, I confused with this. – 梁雨生 Jan 28 '21 at 03:44
  • I'm sorry that the code is out of formatted. But you can get the [ppt](https://shipilev.net/talks/geecon-May2018-jmm.pdf). – 梁雨生 Jan 28 '21 at 03:47
  • 1
    @梁雨生 but the author explains earlier what coherence is, on slide 48. his point is that `val == null` and `return val` are _two independent reads_ and must not be re-ordered, [otherwise double-check locking is broken](https://stackoverflow.com/questions/59208041/do-we-need-volatile-when-implementing-singleton-using-double-check-locking/61166685#61166685) – Eugene Jan 28 '21 at 04:12
  • 1
    that is : a _write_ to `val`, must be seen to have the same value for _both_ reads in `if(val == null)` and `return val`. _Coherent_ thus is : "writes to the single memory location (any write to `val`), must appear to be in total order consistent with **program order**". Where program order here is _first_ `if (val == null)` and _second_ `return val`. This is not easy, I know, but neither it is supposed to. You have the `JLS` to reason about correctly synchronized programs, not `JMM` – Eugene Jan 28 '21 at 04:33
  • Well, I find this question is [duplicated](https://stackoverflow.com/questions/12175254/does-the-java-memory-model-guarantee-visibility-of-intra-thread-writes), and I really mixed something up. There isn't any data race with just a single thread, so the program order(JLS.17.4.3) can guarantee that the return value must be '1'. – 梁雨生 Jan 28 '21 at 09:34
  • @ Eugene Yes, and maybe I should do more reading and think before asking, really thanks for your help, you taught me a lot. – 梁雨生 Jan 29 '21 at 01:25
2

Yes, it is possible for the test method to return 0, if another thread writes to i between the assignment and the return statement:

  1. Thread 1: assign i = 1
  2. Thread 2: assign i = 0
  3. Thread 1: return i (sees the 0 that Thread 2 just wrote)

To prevent this, all access to i, reads and writes, would need to be synchronized on the same condition. Making i volatile is not sufficient to prevent threads from taking turns modifying it.

Note that it's not that Thread 1 "does not see" the i = 1 write; that is guaranteed, because all statements logically execute in program order. However, another thread might change the value after that write happens but before Thread 1 reads it.

chrylis -cautiouslyoptimistic-
  • 75,269
  • 21
  • 115
  • 152