티스토리 뷰
지연 초기화 (lazy initialization)
- 필드 초기화 시점을 그 값이 처음 필요할 때 까지 늦추는 기법
- 정적 필드와 인스턴스 필드에 모두 사용 가능
- 주로 최적화 용도지만, 클래스와 인스턴스 초기화 때 발생하는 위험한 순환 문제를 해결하는 효과도 있음
지연 초기화는 필요할 때까지는 하지 말라
- 클래스나 인스턴스 생성 시 초기화 비용은 줄지만, 지연 초기화하는 필드에 접근하는 비용은 커짐
- 지연 초기화하려는 필드들 중 초기화가 이루어지는 비율에 따라, 실제 초기화 비용에 따라, 초기화 된 각 필드를 얼마나 빈번히 호출하느냐에 따라 지연 초기화가 성능이 느려지게 할 수도 있음
지연 초기화가 필요할 때
- 해당 클래스의 인스턴스 중 그 필드를 사용하는 인스턴스의 비율 < 그 필드 초기화 비용 일 때
- 정말 그런지는 지연 초기화 적용 전후 성능을 측정해봐야함
- 대부분 일반적인 초기화가 지연 초기화보다 낫다.
인스턴스 필드 초기화 방법
1) 일반적인 초기화
//일반적인 방법
private final FieldType field = computeFieldValue();
2) 지연 초기화
지연 초기화가 초기화 순환성을 깨뜨릴 것 같으면 synchronized 접근자를 사용하자.
지연 초기화하는 필드를 둘 이상의 스레드가 공유한다면 반드시 동기화해야함
//지연 초기화 방법 - synchronized 접근자 방식
private FieldType field;
private synchronized FieldType getField() {
if (field == null)
field = computeFieldValue();
return field;
}
3) 지연 초기화 홀더 클래스 관용구
성능 때문에 정적 필드를 지연초기화를 해야한다면 지연 초기화 홀더 클래스 관용구를 사용하자.
private static class Fieldholder {
static final FieldType field = computeFieldValue();
}
private static FieldType getField() { return FieldHolder.field;}
getField가 처음 호출되는 순간 FieldHolder.field가 처음 읽히면서 그때서야 초기화한다. 이 관용구는 getField 메서드가 필드에 접근하면서 동기화를 전혀 사용하지 않으니 성능이 느려지지 않는다.
4) 이중 검사 관용구
성능 때문에 인스턴스 필드를 지연 초기화 해야한다면 이중검사 관용구를 사용하자.
이 관용구는 초기화된 필드에 접근할 때 동기화 비용을 없애준다.
필드의 값을 한번은 동기화 없이 검사하고, 아직 초기화되지 않았다면 두번째는 동기화하여 검사한다.
private volatile FieldType field;
private FieldType getField() {
FieldType result = field;
if (result != null) { // 첫 번째 검사 (락 사용 안 함)
return result;
synchronized(this) {
if (field == null) // 두 번째 검사 (락 사용)
field = computeFieldValue();
return field;
}
}
필드가 초기화된 후로는 동기화하지 않으므로 해당 필드는 volatile로 선언해야한다. [아이템 78]
5) 단일 검사 관용구
반복해서 초기화해도 상관없는 인스턴스 필드를 지연초기화해야 할 때, 이중 검사에서 두번째 검사를 생략.
초기화가 중복해서 일어날 수 있다.
private volatile FieldType field;
private FieldType getField() {
FieldType result = field;
if (result == null)
field = result = computeFieldValue();
return result;
}
6) 짜릿한 단일 검사 관용구
모든 스레드가 필드 값을 다시 계산해도 상관없고 필드 타입이 long과 double을 제외한 기본 타입이라면, 필드 선언에서 volatile을 없애도 된다.
초기화가 스레드 당 한번 더 이뤄질 수 있고 보통 쓰이지 않는 기법
결론
- 대부분의 필드는 지연시키지 말고 바로 초기화하라.
- 성능이나 위험한 초기화 순환을 막기 위해서 지연 초기화를 써야하면 올바르게 사용하자.
- 인스턴스 필드에는 이중 검사 관용구, 정적 필드에는 지연 초기화 홀더 클래스 관용구를 사용하자.
- 반복해 초기화해도 괜찮은 인스턴스 필드는 단일 검사 관용구
'Java > Effective Java' 카테고리의 다른 글
[Effective Java] 85.자바 직렬화의 대안을 찾으라 (0) | 2022.05.10 |
---|---|
[Effective Java] 84.프로그램의 동작을 스레드 스케줄러에 기대지말라 (0) | 2022.05.08 |
[Effective Java] 82.스레드 안전성 수준을 문서화하라 (0) | 2022.05.08 |
[Effective Java] 81.wait와 notify보다는 동시성 유틸리티를 사용하라 (0) | 2022.04.19 |
[Effective Java] 80.스레드보다는 실행자, 태스크, 스트림을 애용하라 (0) | 2022.04.19 |