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 |