Hi Scott
I will try to describe it using happens before order.
To simplify lets assume that the main thread will add all 100 Runnables to executor before executor starts executing. We can also assume that it is multi core machine where the main application thread is executed on cpu1 and the executor thread is on cpu2
main thread - cpu1 | happens before order | executor thread - cpu2 | cpu1 sheepCount1, sheepCount2 | cpu2 sheepCount1, sheepCount1 |
---|
service.execute(....) | | | 0, 0 | initial value, initial value |
... | ... | ... | ... | ... |
service.execute(....) | | | 0, 0 | initial value, initial value |
| Actions in a thread prior to the submission of a Runnable to an Executor happen-before its execution begins | | 0, 0 | 0, 0 |
Thread.sleep(100); | | sheepCount1.getAndIncrement(); sheepCount2++; | 0, 0 | 1, 1 |
... | ... | ... | ... | ... |
| | sheepCount1.getAndIncrement(); sheepCount2++; | 0, 0 | 99, 99 |
| | sheepCount1.getAndIncrement(); sheepCount2++; | 0, 0 | 100, 100 |
System.out.println( | | | | |
sheepCount1+" " | sheepcount1.get() happens before any other set or updates (Ordering of write and read actions on volatile) we will have 100 | | 100, 99 | 100, 100 |
+sheepCount2) | sheepCount2 has no happens before order | | 100, 99 | 100, 100 |
The 100 on sheepCount2 does not have to be visible on the main thread (cpu1) as it is executed after getAndIncrement on (cpu2) and the store buffer does not have to be synchronized with other cores caches as it is just plain variable.
To fix the problem all you have to do is to revert the order of Runnable ()-> {sheepCount1.getAndIncrement(); sheepCount2++};
to ()-> {sheepCount2++; sheepCount1.getAndIncrement()};
Now visibility of sheepCount2++ is guranted by happens before order between writing to sheepCount1 and reading sheepCount1. Or event simpler change sheepCount2 to AtomicInteger.
I will agree that it is very unlike to hit the 99 but still the answer is incorrect as 100, 99 is possible result.
The problem is similar to my puzzle with jcstress where 3 is still possible
result
Observed state Occurrences Expectation Interpretation
1 111,112,823 ACCEPTABLE Actor2 is executed before Actor1
2 33,631 ACCEPTABLE Actor2 is executed after y = 2 and before x = 3 in Actor1
3 26,441 FORBIDDEN y = 2 can not be reordered with x = 3 as we have volatile...
6 21,031,593 ACCEPTABLE Actor2 executed after Actor1
to fix it you have to revert y*x to x*y to have happens before order between writing to volatile x and reading it
Observed state Occurrences Expectation Interpretation
1 119,613,062 ACCEPTABLE Actor2 is executed before Actor1
2 134,699 ACCEPTABLE Actor2 is executed after y = 2 and before x = 3 in Actor1
3 0 FORBIDDEN y = 2 can not be reordered with x = 3 as we have volatile...
6 32,083,827 ACCEPTABLE Actor2 executed after Actor1
What is your opinion?