IT이야기

잘못된 지연 초기화

cyworld 2021. 4. 10. 09:43
반응형

잘못된 지연 초기화


Findbug는 잘못된 지연 초기화를 사용한다고 말했습니다.

public static Object getInstance() {
    if (instance != null) {
        return instance;
    }

    instance = new Object();
    return instance;
}

나는 여기서 잘못된 것을 보지 않는다. findbug의 잘못된 동작입니까, 아니면 뭔가 놓쳤습니까?


Findbug는 잠재적 인 스레딩 문제를 참조하고 있습니다. 다중 스레드 환경에서는 현재 코드로 싱글 톤이 두 번 이상 생성 될 가능성이 있습니다.

이 독서의 많은입니다 여기가 있지만, 설명하는 데 도움이됩니다.

여기서 경쟁 조건은에 있습니다 if check. 첫 번째 호출에서 스레드는에 들어가 if check인스턴스를 만들고 '인스턴스'에 할당합니다. 그러나 if check인스턴스 생성 / 할당과 사이에 다른 스레드가 활성화 될 가능성이 있습니다. 이 스레드 if check는 할당이 아직 발생하지 않았기 때문에를 전달할 수도 있습니다 . 따라서 두 개 (또는 더 많은 스레드가 들어온 경우 그 이상) 인스턴스가 생성되고 스레드는 다른 개체에 대한 참조를 갖게됩니다.


코드가 필요한 것보다 약간 복잡하기 때문에 혼란 스러울 수 있습니다.

편집 : 다른 사람들이 게시 한 것처럼 확실히 스레딩 문제이지만 아래에 참조를 위해 이중 잠금 확인 구현을 게시 할 것이라고 생각했습니다.

private static final Object lock = new Object();
private static volatile Object instance; // must be declared volatile

public static Object getInstance() {
    if (instance == null) { // avoid sync penalty if we can
        synchronized (lock) { // declare a private static Object to use for mutex
            if (instance == null) {  // have to do this inside the sync
                instance = new Object();
            }
        }
    }

    return instance;
}

참고 : JohnKlehm의 이중 잠금 검사 솔루션이 더 좋습니다. 이 답변은 역사적인 이유로 여기에 남겨 둡니다.

실제로는

public synchronized static Object getInstance() {
    if (instance == null) {
        instance = new Object();
    }

    return instance;
}

이를 수정하려면 인스턴스화 주위에 잠금을 설정해야합니다.

LI : 정적 필드의 잘못된 지연 초기화 (LI_LAZY_INIT_STATIC)

이 메서드에는 비 휘발성 정적 필드의 동기화되지 않은 지연 초기화가 포함되어 있습니다. 컴파일러 또는 프로세서가 명령어를 재정렬 할 수 있기 때문에 여러 스레드에서 메서드를 호출 할 수있는 경우 스레드가 완전히 초기화 된 개체를 볼 수 있다고 보장 할 수 없습니다. 문제를 해결하기 위해 필드를 휘발성으로 만들 수 있습니다. 자세한 내용은 Java Memory Model 웹 사이트를 참조하십시오.


게시 된 샘플에 대해 John Klehm에게 감사드립니다.

또한 동기화 된 블록의 개체 인스턴스를 직접 할당하려고 시도 할 수 있습니다.

synchronized (MyCurrentClass.myLock=new Object())

private static volatile Object myLock = new Object();

public static Object getInstance() {
    if (instance == null) { // avoid sync penalty if we can
        synchronized (MyCurrentClass.myLock**=new Object()**) { // declare a private static Object to use for mutex
            if (instance == null) {  // have to do this inside the sync
                instance = new Object();
            }
        }
    }

    return instance;

}

다중 스레딩 문제를 놓쳤습니다.

private static Object instance;

public static synchronized Object getInstance() {
    return (instance != null ? instance : (instance = new Object()));
}

정적 개체가 동기화되지 않았습니다. 또한 귀하의 방법은 지연 초기화가 아닙니다. 일반적으로 당신이하는 일은 당신이 객체의지도를 유지하고, 당신은 요구에 따라 원하는 것을 초기화하는 것입니다. 따라서 필요할 때 호출 (호출)하는 것보다 처음에 모두 초기화하지 않습니다.


1.5 이후 : 인스턴스는 휘발성이어야하며 생성 된 인스턴스를 사용하지 않도록 tmp 변수를 통합해야하지만 초기화가 아직 완료되지 않았습니다.

private static volatile Object myLock = new Object();
private static volatile Object instance;

public static Object getInstance() {
    if (instance == null) {
        Object tmpObj;
        synchronized (myLock) {
            tmpObj = instance;
            if (tmpObj == null) {
                tmpObj = new Object();
            }
        }
        instance = tmpObj;
    }

    return instance;

}

참조 URL : https://stackoverflow.com/questions/6782660/incorrect-lazy-initialization

반응형