1. 지연 초기화(lazy initialization)
필드의 초기화 시점을 그 값이 처음 필요할 때까지 늦추는 기법
값이 전혀 쓰이지 않으면 초기화도 결코 일어나지 않는다.
클래스와 인스턴스 초기화 때 발생하는 위험한 순환 문제를 해결하는 효과가 있다.
최적화에서의 지연초기화
주로 최적화 용도로 사용된다. (최적화는 신중하게 해라 [item67])
클래스 혹은 인스턴스 생성 시의 초기화 비용은 줄지만,
지연 초기화하는 필드에 접근하는 비용이 커진다.
지연 초기화 필드중 결국 초기화가 이뤄지는 비율, 시렞 초기화에 드는 비용, 초기화된 각 필드의 호출 빈도에 따라 성능 판단
지연 초기화가 필요한 시점
해당 클래스의 인스턴스 중 그 필드를 사용하는 인스턴스의 비율이 낮고, 그 필드를 초기화하는 비용이 클 때 (hashCode)
지연 초기화 적용 전후의 성능을 측정
멀티 스레드 환경에서 지연 초기화
지연 초기화 하는 필드를 둘 이상의 스레드가 공유한다면 반드시 동기화!!
2. 초기화 방법
모든 초기화 기법은 기본 타입 필드와 객체 참조 필드 모두에 적용할 수 있다.
수치 기본타입 필드에 적용한다면 null 대신 0과 비교한다.
public class FieldType {
}
public class Initialization {
private static FieldType computeFieldValue() {
return new FieldType();
}
}
a. 일반적인 인스턴스 필드 초기화
대부분의 상황에서 일반적인 초기화가 지연 초기화보다 낫다.
private final FieldType field1 = computeFieldValue();
b. 인스턴스 필드의 지연초기화
지연 초기화가 초기화 순환성(initialization circularity)을 깨뜨릴 것 같으면 synchronized를 단 접근자를 사용하자.
[ initialization circularity ]
Class A in its constructor creates instance of class B, class B creates instance of class C, and class C creates instance of class A.
private FieldType field2;
private synchronized FieldType getField2() {
if (field2 == null)
field2 = computeFieldValue();
return field2;
}
c. 정적 필드용 지연 초기화 홀더 클래스 관용구
private static class FieldHolder {
static final FieldType field = computeFieldValue();
}
private static FieldType getField() { return FieldHolder.field; }
성능때문에 정적 필드를 지연초기화해야 한다면 지연초기화 홀더 클래스(lazy initialization holder class) 관용구를 사용하자
클래스는 클래스가 처음 쓰일 때 비로소 초기화 된다는 특성이 있다.
d. 인스턴스 필드 지연 초기화용 이중검사 관용구
private volatile FieldType field4;
private FieldType getField4() {
FieldType result = field4;
if (result != null) // 첫 번째 검사 (락 사용 안 함)
return result;
synchronized(this) {
if (field4 == null) // 두 번째 검사 (락 사용)
field4 = computeFieldValue();
return field4;
}
}
성능 때문에 인스턴스 필드를 지연 초기화해야 한다면 이중검사(double-check) 관용구를 사용하라
초기화된 필드에 접근할 때의 동기화 비용을 없애준다.
첫번째 검사 => 두번째 검사(필드가 초기화 되지 않았을 때)
필드가 초기화 된 후로는 동기화 하지 않으므로 해당 필드는 반드시 volatile로 선언한다.
result 지역변수 : 필드가 이미 초기화된 상황에서는 이 필드를 딱 한번만 읽도록 보장하는 역할을 한다.
e. 단일 검사 관용구
private volatile FieldType field5;
private FieldType getField5() {
FieldType result = field5;
if (result == null)
field5 = result = computeFieldValue();
return result;
}
private static FieldType computeFieldValue() {
return new FieldType();
}
이중검사의 변종이다.
반복해서 초기화해도 상관없는 인스턴스 필드를 지연 초기화 하는 경우.
초기화가 중복해서 일어날 수 있다.
근데 반복해서 초기화할꺼면 왜 지연초기화를 하는거지..
f. 짜릿한 단일검사(racy single-check) 관용구
private FieldType field5;
private FieldType getField5() {
FieldType result = field5;
if (result == null)
field5 = result = computeFieldValue();
return result;
}
private static FieldType computeFieldValue() {
return new FieldType();
}
모든 스레드가 필드의 값을 다시 계산해도 상관없고 필드의 타입이 long과 double을 제외한 다른 기본 타입이라면, 단일 검사의 필드선언에서 volatile을 없애도 된다.
어떤 환경에서는 필드 접근 속도를 높여주며
초기화가 스레드당 최대 한번 더 이뤄질 수 있다,
'Reading Record > 이펙티브자바' 카테고리의 다른 글
[아이템 84] 프로그램의 동작을 스레드 스케줄러에 기대지 말라 (0) | 2020.07.11 |
---|---|
[아이템 82] 스레드 안전성 수준을 문서화하라 (0) | 2020.07.11 |
[아이템 32] 제네릭과 가변인수를 함께 쓸 때는 신중하라 (2) | 2020.07.04 |
[아이템43~44] 람다 사용법 (0) | 2020.07.04 |
[아이템 81] wait와 notify 보다는 동시성 유틸리티를 애용하라 (0) | 2020.07.03 |