1

Consider the following scenario borrowed from Memory Consistency - happens-before relationship in Java:

package happen.before;

public class HappenBeforeRelationship {

private static int counter = 0;

private static void threadPrintMessage(String msg){
    System.out.printf("[Thread %s] %s\n", Thread.currentThread().getName(), msg);
}

public static void main(String[] args) {
    threadPrintMessage("Increase counter: " + ++counter);
    Thread t = new Thread(new CounterRunnable());
    t.start();
    try {
        t.join();
    } catch (InterruptedException e) {
        threadPrintMessage("Counter is interrupted");
    }
    threadPrintMessage("Finish count: " + counter);
}

private static class CounterRunnable implements Runnable {
    @Override
    public void run() {
        threadPrintMessage("start count: " + counter);
        counter++;
        threadPrintMessage("stop count: " + counter);
    }
}

I know there is a rule in JLS guarantees that Thread.start happens before all actions in the started thread.

When a statement invokes Thread.start, every statement that has a happens-before relationship with that statement also has a happens-before relationship with every statement executed by the new thread. The effects of the code that led up to the creation of the new thread are visible to the new thread.

But it does not claim that statements before Thread.start has a happens-before relationship with it.

So I wonder that could Thread.start be reordered so that the program could not get the expected output (counter=2)? If not, which part of JLS specifies that Thread.start could not be reordered?


Another question:

What happens if join() is put after threadPrintMessage("Finish count: " + counter);? Is it possible that stop count: 1 is printed?

Community
  • 1
  • 1
chain ro
  • 737
  • 2
  • 10
  • 21
  • 1
    Irrespective of reordering of the start, the output is not necessarily 2, because visibility of updates to counter is not guaranteed. – Andy Turner Jul 17 '16 at 16:56
  • @AndyTurner, could you review the second answer in http://stackoverflow.com/questions/16248898/memory-consistency-happens-before-relationship-in-java? It points out that `JLS guarantees that calling t.start() makes the change to x visible in t.run() so y is guaranteed to be assigned 1`, so is it a wrong statement? – chain ro Jul 17 '16 at 17:03
  • @chainro that's definitely correct. Top to bottom, as I said. Before the `Thread.start` you are single-threaded so natural order is respected – Dici Jul 17 '16 at 17:09
  • @chainro apologies for not being more explicit: updates to `counter` by the new thread are not guaranteed to be visible to the main thread. – Andy Turner Jul 17 '16 at 18:36
  • @AndyTurner, I wonder whether Thread.start() has a similar semantic as lock? You know that if we release the lock, all variables after locking, even non-volatile ones, could be refreshed. – chain ro Jul 18 '16 at 02:48
  • @Andy Turner: since there is a `join` operation, I don’t see, why the updates made by the new thread shouldn’t be visible to the main thread (unless an `InterruptionException` occurred). Is there any specific reason beyond that, why you think, that updates are not guaranteed? – Holger Jul 18 '16 at 13:31
  • @Holger I've been reading some more, and it appears that `join` creates a happens-before. So... cheerfully withdrawn. – Andy Turner Jul 18 '16 at 13:32

1 Answers1

0

There is an ordering relationship between the actions before the Thread.start invocation and the start of the new thread due to JLS§17.4.5:

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

and later on in the same section, the already mentioned in your question guaranty:

  • A call to start() on a thread happens-before any actions in the started thread.

Due to the transitive nature of happens-before relationships, you have a happens-before relationship between the action of the main thread before invoking start() and the actions within the started thread. Similarly, you have a happens-before relationship between the actions of the started thread and a successful return of the main thread’s join invocation.

In other words, as long as you don’t experience an InterruptedException, the updates are properly ordered and the printed result will be 2. This, however does not imply that the actions are never reordered as the JLS§17.4.5 continues:

It should be noted that the presence of a happens-before relationship between two actions does not necessarily imply that they have to take place in that order in an implementation. If the reordering produces results consistent with a legal execution, it is not illegal.

In other words, happens-before relationships are a high-level concept, that allows you to determine legal results of a program execution. If, like in this example, having 2 in the counter at the end of the program is the only legal outcome (as said, assuming that no interruption occurred), the JVM might arrange the code as much as it wishes, as long as the program will produce the legal outcome of having 2 in the counter at the end.

You should stop bothering about what could be reordered or not. That’s an unimportant implementation detail. In the absence of proper happens-before relationships you might experience updates that appear out-of-order, but also might miss updates or create inconsistent states, so it makes no sense to focus on one implementation detail that could lead to unintended side effects in broken programs. Instead, focus on what makes a correct program.

Holger
  • 285,553
  • 42
  • 434
  • 765
  • Why do you think `++counter` comes before `thread.start` in program order? In other words, I do not think `++counter` *happens-before* `thread.start`. – chain ro Jul 18 '16 at 12:00
  • 1
    What do you think, is the “program order”? That’s the order of your statements, as you have written. It’s the same order, the statements would be executed if no other threads existed. Even your question implicitly acknowledges that `++counter` comes before `thread.start` as I never said that. It is you who thought of this ordering when I talked about program order without naming any specific actions. – Holger Jul 18 '16 at 12:32
  • In the program, `++counter` is put before `thread.start`, but the program order only guarantees the order of variables that have dependencies https://docs.oracle.com/javase/specs/jls/se8/html/jls-17.html#jls-17.4.3. – chain ro Jul 18 '16 at 12:54
  • 1
    @chain ro: in that linked section, there is not the slightest mentioning of what you claim. It’s not even clear what your term “*variables that have dependencies*” should stand for. There is no such thing. And anyway, if you think, you now better than everyone else, why did you open a question on Stackoverflow? – Holger Jul 18 '16 at 13:07
  • JLS explicitly points out that: each read r of a variable v sees the value written by the write w to v such that: w comes before r in the execution order, and there is no other write w' such that w comes before w' and w' comes before r in the execution order. I open a question on Stackoverflow because there are many inconsistent answers for similar questions so that I am very confused. I propose my understanding of JLS, and I want to know what is the exact answer. – chain ro Jul 18 '16 at 13:20
  • For instance, @Andy Turner thinks `counter` will not be `2` finally without exception, but @Holger thinks `counter` should be `2`. – chain ro Jul 18 '16 at 13:22
  • 1
    @chain ro: what you have cited, talks about a single variable only. I don’t see, how your can derive a statement about something like “*variables that have dependencies*” from that. – Holger Jul 18 '16 at 13:32
  • 1
    @chain ro: My answer mainly focused on your original question about `start()` and preceding operations, whereas Andy Turner talked about the new thread’s action and the end of the program. Since there is a `join()` the updates should be visible, unless an `InterruptedException` occurred (which shouldn’t in this simple program). I already addressed that possibility in my answer. I directed a comment to him, asking whether there are other reasons for his statement. – Holger Jul 18 '16 at 13:35
  • sorry, I shouldn't make that statement. I have updated the question. – chain ro Jul 18 '16 at 13:37
  • 1
    Of course, if you move `join` after the `counter` access, there is no guaranty about the value anymore. You shouldn’t change question in this way. The reason why other Q&A’s might read contradicting, is explained at the end of my answer: talking about reordering is misleading. The only relevant thing is which partial order (chain of *happens-before* relationships) is guaranteed. – Holger Jul 18 '16 at 13:50
  • thank you very much. Actually, in the end I realize that my question is not clear, what I want to ask is the final one. I didn't realize that there are multiple print statements. So that maybe why we cannot reach a consistency quickly. – chain ro Jul 18 '16 at 14:03