I am currently studying for a concurrent programming exam and don\'t understand why the output of this program is 43. Why is x = y + 1
executed before t.s
According to JMM:
A call to start() on a thread happens-before any actions in the started thread.
and
If x and y are actions of the same thread and x comes before y in program order, then hb(x, y).
Definition of program order is this:
Among all the inter-thread actions performed by each thread t, the program order of t is a total order that reflects the order in which these actions would be performed according to the intra-thread semantics of t.
The inter-thread semantic is well defined concept in JMM. It means that the order of instructions in a program performed by each thread must be preserved as it is written in the program text.
Applying all these to your case:
t.start();
hb x = y + 1;
//program order
t.start();
hb y = x;
// happens-before rule specified here
Without additional synchronization we cannot say how x = y + 1;
and y = x;
relates to each other (From JMM standpoint).
If you are trying to answer the question "What can happen at runtime in my case?". Can happen lots of things... Take a look at this asnwer. Runtime can perform non-trivial optimisations which are consitent with JMM.
Anyway if you are interested about internals you can take a look at this article (Memory Barriers Can Be Avoided). As you can see in the generated assembly, no memory barrier is applied when performing volatile read. I mean that runtime can optimize in anyway... As long the JMM rules are preserved...
t.start
is before x = y + 1
, it does not gurantee every line of code in run()
method will be executed before x = y + 1
.
In fact, the printed result is uncertain without syncronization because of race condition, it could be 1
or 43
.
I would like to add that the code in the main method runs in the Main thread and Thread t doesn't block execution of Main in your example. That's why the line x = y + 1
might be executed faster than the body of the Thread t (as @davidxxx already pointed out).
You can observe different behavior if you add t.join()
after t.start():
...
t.start();
t.join();
In this situation, the Main thread will wait for the Thread t to complete and the output will be 1.
There is no synchronization, no volatile
fields, no locking, no atomic fields. The code can execute in any order.
Yes, t.start()
will execute before x = y + 1
. But starting the thread doesn't mean the thread body executes before x = y + 1
. It could run before, or after, or interleaved with the rest of main()
.
If I understand the program order rule (each action in a thread happens-before every action in that thread that comes later in the program order)
No it doesn't.
Here you don't have one thread but two threads :
the main thread and the thread created and started by the main thread :
Thread t = new Thread() {...};
t.start();
And living threads of the JVM are by design concurrent.
If the two statements had been executed in the same thread, it would be safe to assume that the value printed out would be "1". But if the two statements are executed in separate threads, the value printed out might well be "0", because there's no guarantee that thread A's change to counter will be visible to thread B — unless the programmer has established a happens-before relationship between these two statements.
The happens-before relationship occurs only if all statements are performed by the same thread or if you explicitly create the happens-before relationship.
Extract from the Memory Consistency Errors tutorial :
There are several actions that create happens-before relationships. One of them is synchronization, as we will see in the following sections.
As said above, the statements are performed by two threads.
And you don't explicitly synchronize statements.
So you have a race condition between the threads and as a general rule, the execution order should be considered as unpredictable.
Now in practice, for a such short statement : x = y + 1
:
t.start();
x = y + 1;
The assignment of the adding operation is so short to be executed as it is very likely to occur before that the thread referenced by t
be effectively run.
Besides, modern CPUs have multiple cores.
So if the CPU has thread availability, the main thread will not be paused to make the new thread running.
The two threads will so be executed at the "same time".
But as x = y + 1;
is much faster to be executed as starting and running a thread, the first statement can only finish before the second one.