Reputation: 11
After reading through JLS and looking through multiple scenarios I can't get quite right which rules does the Java Memory models obeys for intra-thread semantics
Consider this code just for example purpose:
public class CharIndexer {
public Map<char, int> charLastIndex;
public void changeCount(String phrase) {
Map<char, int> newCharLastIndex = new HashMap<char, int>();
for (int i = 0; i < s.length(); i++){
newCharLastIndex.put(s.charAt(i),i);
}
charLastIndex = newCharLastIndex;
}
}
In a scenario with multiple threads holding a reference to the same instance of CharIndexer, reading the field charLastIndex while one of them invokes changeCount method.
Would it be a valid reordering if the assignment to the charLastIndex field, the last assignment on the method, was done before the for block?
This would make possible for the reading threads to see a Map which was still not populated.
While I agree the visibility guarantee should be made explicit with a volatile keyword, do the intra-thread semantics allow such reordering?
Single-thread execution of both orders grant the same results, but which rules do govern the intra-thread reordering that would avoid the reordering of the two for blocks in this implementation:
public class CharIndexer {
public Map<char, int> charLastIndex;
public void changeCount(String phrase) {
Map<char, int> newCharLastIndex = new HashMap<char, int>();
for (int i = 0; i < s.length(); i++){
newCharLastIndex.put(s.charAt(i),i);
}
// just changing values around
foreach(Map.Entry<char,int> charEntry : newCharLastIndex) {
charEntry.setValue(charEntry.getValue() * 10);
}
charLastIndex = newCharLastIndex;
}
}
I'm trying to wrap my head around how far does the JIT analysis goes or if I'm ignorant of the particular set of rules for intra-thread semantics.
Upvotes: 1
Views: 156
Reputation: 159260
With no happen-before boundaries or synchronization blocks in effect, the order of operations, as seen by another thread, can be out-of-order.
The best way to explain it, is that your HashMap
object and your CharIndexer
object may be located in different parts of computer memory.
On a multiple-CPU server, memory access is cached independently by each CPU, so when CPU #1 run the changeCount()
method, all it's actions are done in the CPU 1 Cache.
Eventually, that cache is flushed to main RAM, and other CPU's can see it, once they reload their cache from main RAM.
But, the cache section holding the HashMap
, and the cache section holding the CharIndexer
, may not flush+reload at the same time. So CPU #2 may see the update to CharIndexer
before is sees the update to the referenced HashMap
.
That is why you need to ensure that the building of the HashMap
happens-before the assignment of the charLastIndex
value, or that both will happen-before any use of the charLastIndex
value.
Upvotes: 1