While writing some java article I'm trying to reproduce re-ordering in case of unsynchronized object costruction in multithreaded environment. The case when a heavy object is constructed w/o synchonization/volatiles/finals and other threads get access to it right after constructor call. Here is the code I try:
public class ReorderingTest {
static SomeObject<JPanel>[] sharedArray = new SomeObject[100];
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
String name = "watcher" + i;
new Thread(new Watcher(name)).start();
System.out.printf("watcher %s started!%n", name);
}
}
static class Watcher implements Runnable {
private String name;
Watcher(String name) {
this.name = name;
}
public void run() {
while (true) {
int randomIndex = (int) (Math.random() * sharedArray.length);
SomeObject<JPanel> item = sharedArray[randomIndex];
if (item == null) {
//System.out.printf("sharedArray[%s]=null%n", randomIndex);
double r = 1 + Math.random() * 1000;
sharedArray[randomIndex] = new SomeObject<JPanel>(
new JPanel(), UUID.randomUUID().toString(), r, (float)r * 33, (long)r);
} else {
//System.out.printf("sharedArray[%s]=<obj>!%n", randomIndex);
if (item.value == null ||
(item.stringField == null) ||
(item.doubleField == 0) ||
(item.floatField == 0) ||
(item.longField == 0)
) {
System.err.printf("watcher %s sees default values: %s!%n", name, item);
} else {
// fully initialized! run new construction process
double r = 1 + Math.random() * 1000;
sharedArray[randomIndex] = new SomeObject<JPanel>(
new JPanel(), UUID.randomUUID().toString(), r, (float)r * 37, (long)r);
}
}
/*try {
TimeUnit.NANOSECONDS.sleep(randomIndex);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}*/
}
}
}
static class SomeObject<V> {
V value;
String stringField;
double doubleField;
float floatField;
long longField;
SomeObject(V value, String stringField, double doubleField, float floatField, long longField) {
this.value = value;
this.stringField = stringField;
this.doubleField = doubleField;
this.floatField = floatField;
this.longField = longField;
}
@Override
public String toString() {
return "SomeObject{" +
"value=" + value == null ? "null" : "<obj>" +
", stringField='" + stringField + '\'' +
", doubleField=" + doubleField +
", floatField=" + floatField +
", longField=" + longField +
'}';
}
}
}
-But no effect so far, I've tried on different 2,4 and 8 core Intel/AMD PCs with windows, ran test for a few hours - no reordering effect - System.err.printf("watcher %s sees ...") - is not called, static sharedArray[randomIndex] reference always contains fully constructed values.
What's wrong? How to reproduce this?
Here is a nice article which should show reordering on x86 (quite a feat because the x86 memory model is pretty much "safe"):
http://bartoszmilewski.wordpress.com/2008/11/05/who-ordered-memory-fences-on-an-x86/
Your example will not show reordering. The compiler will not reorder to "store object reference after allocation but before construction" because the constructor could throw and thus the reference would need to be reverted. Some Processors may reorder this, but no intel compatible ones, due to the given guaranties.
Re-ordering is not guaranteed. It usually only occurs when the JIT determines there might be a performance gain.
In your case, what you are looking for is not re-ordering but an Object which doesn't appear to have been correctly initialised.
Your objects are highly likely to use a new memory location each time (or at least one which is not in cache) On x86/x64 architectures, I have found that the cache will always be correct the first time it has to load memory when was updated by another thread.
re-ordering does not necessarily happen. and when it does, it's hard to observe. you sample size of 100
is too tiny. Try a billion for starters.
Suppose re-ordering happens, at the time of write, reference is assigned first, then fields are populated. Your read process reads the reference first, then reads the fields. So write and read follow the same order, it's almost impossible for read to catch up and get ahead of write.
You can address the issue by
- pause in constructor (therefore pause in write)
- write and read fields in opposite direction
Still, reordering may not occur and you won't be able to observe it.
could you try to declare sharedArray
as non-volatile? It seems to me that handing a volatile reference to a correctly constructed object is safe.
来源:https://stackoverflow.com/questions/6264466/jvm-reordering-visibility-effect-test