[아이템83] 지연 초기화는 신중히 사용하라

2024. 12. 19. 02:56·1️⃣ 백앤드/이펙티브 자바

1. 지연 초기화

지연 초기화는 필드의 초기화 시점을 그 값이 처음 필요할 때까지 늦추는 기법이다. 값이 쓰이지 않으면 초기화도 일어나지 않게 된다. 주로 최적화 용도로 쓰이지만, 클래스와 인스턴스 초기화 때 발생하는 위험한 순환 문제를 해결하는 효과도 있다.

지연 초기화는 필요할 때까지 하지 말자.

클래스의 인스턴스 생성 초기 비용은 훨씬 줄지만, 필드에 접근하는 비용은 커진다. 오히려 성능이 느려질 수 있다. 그래서 그 필드를 사용하는 인스턴스의 비율이 낮으며 그 필드를 초기화하는 비용이 컸을 때 지연 초기화가 제 역할을 한다. 이를 위해 지연 초기화 적용 전후의 성능을 측정해봐야 한다.

2. 멀티쓰레드 환경에서의 지연 초기화

멀티쓰레드 환경에서 지연 초기화를 하기란 까다롭다. 지연 초기화하는 필드를 둘 이상의 쓰레드가 공유한다면 반드시 동기화해야한다. 하지만, 대부분의 상황에서 일반적인 초기화가 지연 초기화보다 낫다. 그래서 신중해야 한다는 것이다. 인스턴스 필드를 선언할 때 수행하는 일반적인 초기화를 살펴보자.

private final FieldType field = computeFieldValue();

그리고, 지연 초기화로 변경시켜보자.

private FieldType field;

private synchronized FieldType getField(){
    if (field == null)
        field = computeFieldValue();
    return field;
}

지연 초기화가 초기화 순환성을 깨뜨릴 것 같으면, synchronized 를 단 접근자를 사용하자. 그리고, 성능 때문에 정적 필드를 지연 초기화해야 한다면 지연 초기화 홀더 클래스 관용구를 사용하자. 이는 클래스가 처음 쓰일 때 비로소 초기화된다는 특성을 이용한 관용구이다.

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

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

getField 가 처음 호출되는 순간, FieldHolder.field 가 처음 읽히면서 비로소 FieldHolder 클래스가 초기화된다. getField 메서드가 필드에 접근하면서 동기화를 전혀 하지 않아 성능이 느려질 수 없다. 그리고, 성능 때문에 정적 필드를 지연 초기화해야 한다면 이중검사 관용구를 사용하자. 초기화된 필드에 접근할 때 동기화 비용을 없애준다. 필드 값을 두 번 검사하는데, 한 번은 동기화 없이, 두 번째는 동기화 하여 검사한다.

private volatile FieldType field;

private FieldType getField() {
    FieldType result = field;
    if (result != null)    // 첫 번째 검사 (락 사용 안 함)
        return result;

    synchronized(this) {
        if (field == null) // 두 번째 검사 (락 사용)
            field = computeFieldValue();
        return field;
    }
}

필드가 초기화된 후로는 동기화하지 않으므로, 해당 필드는 반드시 최신의 값을 불러오는 volatiole 키워드를 통해 선언해야 한다. 만약에, 반복해서 초기화해도 상관없는 인스턴스 필드를 지연 초기화해야 하는 경우라면 이중검사에서 두 번째 검사를 생략해도 된다. 이를 단일검사 관용구라고 한다.

private volatile FieldType field;

private FieldType getField() {
    FieldType result = field;
    if (field == null)
    	field = result = computeFieldValue();
    return result;
}

3. 정리

대부분의 필드는 지연시키지 말고 곧바로 초기화하자. 성능 때문이라던지 위험한 초기화 순환을 막기 위해 꼭 지연 초기화를 해야한다면, 올바른 지연 초기화 기법을 사용해야 한다. 인스턴스 필드에는 이중검사 관용구를, 정적 필드에는 지연 초기화 홀더 클래스를 관용구로 사용하자. 반복해 초기화해도 괜찮은 인스턴스 필드에는 단일검사 관용구를 고려하자.

지연 초기화를 해야한다면 올바르게 사용하자.

'1️⃣ 백앤드 > 이펙티브 자바' 카테고리의 다른 글

[아이템84] 프로그램의 동작을 스레드 스케줄러에 기대지 말라  (0) 2024.12.19
[아이템82] 스레드 안전성 수준을 문서화해라  (0) 2024.12.19
[아이템81] wait 와 notify 보다는 동시성 유틸리티를 애용해라  (0) 2024.12.18
[아이템80] 스레드보다는 실행자, 태스크, 스트림을 애용하라  (0) 2024.12.16
[아이템79] 과도한 동기화는 피하라  (0) 2024.12.12
'1️⃣ 백앤드/이펙티브 자바' 카테고리의 다른 글
  • [아이템84] 프로그램의 동작을 스레드 스케줄러에 기대지 말라
  • [아이템82] 스레드 안전성 수준을 문서화해라
  • [아이템81] wait 와 notify 보다는 동시성 유틸리티를 애용해라
  • [아이템80] 스레드보다는 실행자, 태스크, 스트림을 애용하라
HOZINU
HOZINU
주니어 백앤드 개발자의 세상만사 이모저모. 주로 개발 이야기를 다룸.
  • HOZINU
    백엔드 탐험 일지
    HOZINU
  • 전체
    오늘
    어제
  • 블로그 메뉴

    • ⛪ HOME
    • 🌍 GITHUB
    • 카테고리 (73)
      • 1️⃣ 백앤드 (72)
        • 이펙티브 자바 (72)
      • 2️⃣ CS (0)
        • 운영체제 (0)
        • 네트워크 기초 (0)
        • 네트워크 응용 (0)
        • SSL & PKI (0)
        • 기타 (0)
      • 3️⃣ 코딩테스트 (0)
      • 4️⃣ 개인공부 (0)
        • MSA (0)
        • REDIS (0)
      • 5️⃣ 일상이야기 (1)
  • 인기 글

  • 태그

    빌더
    equals
    hashcode
    컴포지션
    CLONE
    Comparable
    optional
    멤버클래스
    캡슐화
    Cleaner
    로타입
    finalizer
    정보은닉
    try-with-resources
    맥북
    의존객체
    싱글턴
    계층구조
    표준예외
    정적 팩터리 메서드
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.2
HOZINU
[아이템83] 지연 초기화는 신중히 사용하라
상단으로

티스토리툴바