Podczas majstrowania przy testach jednostkowych dla wysoce współbieżnej klasy singleton natknąłem się na następujące dziwne zachowanie (testowane na JDK 1.8.0_162):
private static class SingletonClass {
static final SingletonClass INSTANCE = new SingletonClass(0);
final int value;
static SingletonClass getInstance() {
return INSTANCE;
}
SingletonClass(int value) {
this.value = value;
}
}
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
System.out.println(SingletonClass.getInstance().value); // 0
// Change the instance to a new one with value 1
setSingletonInstance(new SingletonClass(1));
System.out.println(SingletonClass.getInstance().value); // 1
// Call getInstance() enough times to trigger JIT optimizations
for(int i=0;i<100_000;++i){
SingletonClass.getInstance();
}
System.out.println(SingletonClass.getInstance().value); // 1
setSingletonInstance(new SingletonClass(2));
System.out.println(SingletonClass.INSTANCE.value); // 2
System.out.println(SingletonClass.getInstance().value); // 1 (2 expected)
}
private static void setSingletonInstance(SingletonClass newInstance) throws NoSuchFieldException, IllegalAccessException {
// Get the INSTANCE field and make it accessible
Field field = SingletonClass.class.getDeclaredField("INSTANCE");
field.setAccessible(true);
// Remove the final modifier
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
// Set new value
field.set(null, newInstance);
}
Ostatnie 2 wiersze metody main () nie zgadzają się co do wartości INSTANCE - domyślam się, że JIT całkowicie pozbył się tej metody, ponieważ pole jest statyczne. Usunięcie końcowego słowa kluczowego powoduje, że kod wyjściowy ma prawidłowe wartości.
Odkładając na bok swoją sympatię (lub jej brak) na singletony i na chwilę zapominając, że użycie tego rodzaju refleksji prosi o kłopoty - czy moje założenie jest słuszne, jeśli chodzi o to, że optymalizacja JIT jest winna? Jeśli tak - czy są one ograniczone tylko do statycznych pól końcowych?
static final
polem. Poza tym nie ma znaczenia, czy hack odbicia zrywa się z powodu JIT czy współbieżności.