Beim Herumspielen mit Komponententests für eine hochkonkurrierende Singleton-Klasse bin ich auf das folgende seltsame Verhalten gestoßen (getestet mit 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);
}
Die letzten beiden Zeilen der main () -Methode stimmen nicht mit dem Wert von INSTANCE überein. Ich vermute, dass JIT die Methode vollständig entfernt hat, da das Feld statisch final ist. Durch Entfernen des letzten Schlüsselworts gibt der Code korrekte Werte aus.
Wenn Sie Ihr Mitgefühl (oder das Fehlen davon) für Singletons beiseite lassen und für eine Minute vergessen, dass die Verwendung einer solchen Reflexion Ärger bereitet - ist meine Annahme richtig, dass JIT-Optimierungen schuld sind? Wenn ja - sind diese nur auf statische Endfelder beschränkt?
static final
Feld. Außerdem spielt es keine Rolle, ob dieser Reflection-Hack aufgrund von JIT oder Parallelität unterbrochen wird.