동시성이 높은 싱글 톤 클래스에 대한 단위 테스트를 할 때 나는 다음과 같은 이상한 행동을 발견했습니다 (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);
}
main () 메소드의 마지막 두 줄은 INSTANCE 값에 동의하지 않습니다. 필자는 JIT가 필드가 정적 최종이기 때문에 메소드를 완전히 제거했다고 생각합니다. 최종 키워드를 제거하면 코드 출력이 올바른 값이됩니다.
싱글 톤에 대한 동정심 (또는 그 부족)을 버리고 이와 같은 반사를 사용하는 것이 문제를 요구한다는 것을 잊어 버립니다. 그렇다면 정적 최종 필드로만 제한됩니까?
static final
필드 가있는 클래스 만 있습니다 . 게다가 JIT 또는 동시성으로 인해이 반사 핵이 중단되는지 여부는 중요하지 않습니다.