83. 지연 초기화는 신중히 사용하라

2025. 3. 5. 21:36

지연 초기화 : 필드의 초기화 시점을 그 값이 처음 필요할 때까지 늦추는 방법.

 

최선의 조언은 "지연 초기화는 필요할 때까지는 하지 말라"이다. 지연 초기화는 양날의 검이다. 지연초기화하는 필드에 접근하는 비용이 커지기 때문이다. 실제로는 성능을 느려지게 할 수도 있다.

public class LazyExample {
    private String data;

    public String getData() {
        if (data == null) { // 초기화 체크
            data = "지연 초기화된 값";
        }
        return data;
    }
}

 

그럼에도 해당 클래스의 인스턴스 중 그 필드를 사용하는 인스턴스의 비율이 낮은 반면, 그 필드를 초기화하는 비용이 크다면 지연 초기화 하는 것이 좋다. 하지만 정말 좋은지 확인하는 방법은 적용 전후의 성능을 측정해보는 것이다.

 

멀티스레드 환경에서는 지연 초기화를 하기가 까다롭다. 둘 이상의 스레드가 지연 초기화하는 필드를 공유하면 반드시 동기화 해야 한다. 대부분의 상황에서 일반적인 초기화가 지연 초기화보다 낫다. (final 키워드 사용해서 즉시 초기화) => 코드가 단순하고 if문 같은 추가적인 검사 비용이 들지 않음.

 

지연 초기화가 초기화 순환성을 깨뜨릴 것 같으면 synchronized를 단 접근자를 사용하자. synchronized를 통해 단 한 번만 초기화 되도록 함으로써 순환 참조 방지.

 

성능 때문에 정적 필드를 지연초기화 해야 한다면 지연 초기화 홀더 클래스 관용구를 사용하자. 클래스는 클래스가 처음 쓰일 때 비로소 초기화된다는 특성을 이용한 관용구다.

public static class FieldHolder {
    static final FieldType field = computeFieldValue();
}

private static FieldType getField() { return FieldHolder.field; }

 

성능 때문에 인스턴스 필드를 지연초기화 해야 한다면 이중검사 관용구를 사용하라.

첫 검사에서 동기화 없이 검사하고, 두 번째에서 동기화하여 검사하는 방식이다. 두 번째 검사에서도 필드가 초기화 되지 않았을 경우에만 필드를 초기화 한다. 해당 필드는 메모리 가시성 해결을 위해 반드시 volatile로 선언해야 한다.

public class ThreadSafeLazySingleton {
    private static volatile ThreadSafeLazySingleton instance;

    private ThreadSafeLazySingleton() {
        System.out.println("ThreadSafeLazySingleton 인스턴스 생성!");
    }

    public static ThreadSafeLazySingleton getInstance() {
        if (instance == null) {
            synchronized (ThreadSafeLazySingleton.class) {
                if (instance == null) {
                    instance = new ThreadSafeLazySingleton();
                }
            }
        }
        return instance;
    }

 

BELATED ARTICLES

more