Skip to content

Latest commit

 

History

History
198 lines (101 loc) · 4.21 KB

83_지연_초기화는_신중히_사용하라_김재준.md

File metadata and controls

198 lines (101 loc) · 4.21 KB

ITEM 83

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

지연초기화(lazy initialization)

  • 필드의 초기화 시점을 그 값이 처음 필요할 때까지 늦추는 기법
  • 주로 최적화 용도로 사용
  • 클래스와 인스턴스 초기화 때 발생 하는 위험한 순환문제를 해결하는 효과도 있음

지연초기화는 양날의 검이다.

지연초기화를 사용한다면 인스턴스 생성 시의 초기화 비용은 줄지만 그 대신 필드에 접근하는 비용은 커진다.

즉 지연 초기화 필드중 초기화가 이뤄지는 비율에 따라, 실제 초기화에 드는 비용에따라, 초기화된 각 필드를 얼마나 빈번히 호출하느냐에 따라

지연 초기화가 실제로는 성능을 느려지게 할 수 있다.

따라서 지연초기화는

  1. 해당 클래스의 인스터스 중 그 필드를 사용하는 인스턴스의 비율이 낮고
  2. 그 필드를 초기화하는 비용이 크다면

지연초기화를 사용 하자.

하지만 이러한 경우를 알기 위한 방법은 지연초기화 적용 전후의 성능을 측정해보는 것 이 외에는 없다. ㅜ.ㅜ

또한 멀티스레드 환경에서는 지연 초기화 하는 필드를 둘 이상의 스레드가 공유한다면

어떤 형태로든 반드시 동기화 해야한다.

초기화 방법들

일반적 초기화 예제

public class TestClass {
    private final Member member = createMember();

    private Member createMember() {
        return new Member("KJJ");
    }
}

대부분의 상황에서 일반적인 초기화가 지연 초기화보다 낫다.

인스턴스 필드의 지연초기화

public class TestClass {
    private Member member;

    public synchronized  Member getMember(){
        if(member==null){
            member = createMember();
        }
        return member;
    }
    private Member createMember() {
        return new Member("KJJ");
    }
}

지연 초기화가 초기화 순환성(initialization circularity) 을 깨뜨릴 것 같으면 synchronized 를 단 접근자를 사용하자.

정적 필드용 지연 초기화 홀더 클래스 관용구

public class TestClass {

    static final private Member member = createMember();

    private static Member createMember() {
        System.out.println("init");
        return new Member("KJJ");
    }

    public static Member getMember(){
        return TestClass.member;
    }
}

해당 방식은 클래스는 클래스가 처음 쓰일 때 비로소 초기화 된다는 특성을 이용한 관용구다.

이러한 방식은 getMember 메서드가 필드에 접근하면서 동기화를 전혀 하지않으니 성능이 느려질 거리가 전혀 없다.

인스턴스 필드 지연 초기화용 이중검사 관용구

public class TestClass {

    private volatile Member member;

    private Member createMember() {
        return new Member("KJJ");
    }

    public Member getMember() {
        Member result = member;
        if(result!=null){
            return result;
        }
        synchronized (this){
            if(member == null){
                member = createMember();
            }
            return member;
        }
    }
}

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

해당 관용구는 초기화된 필드에 접근할 때의 동기화 비용을 없애준다.

필드가 초기화 된 후로는 동기화 하지 않으므로 해당 필드는 반드시 volatile로 선언한다.

result 지역변수는 필드가 이미 초기화된 상황에서는 이 필드를 딱 한번만 읽도록 보장하는 역할을 한다

단일검사 관용구

public class TestClass {

    private volatile Member member;

    private Member createMember() {
        return new Member("KJJ");
    }

    public Member getMember() {
        Member result = member;
        if (result == null) {
            member = result = createMember();
        }
        return result;

    }

}

여러번 초기화 되어도 상관이 없다면 해당 방식을 사용하자.(초기화가 중복해서 일어날 수 있음)