지연 초기화는 필드의 초기화 시점을 그 값이 처음 필요햔 시점까지 늦추는 것이다. 지연 초기화는 인스턴스 생성시 초기화 비용을 줄일 수 있지만, 지연 초기화하는 필드에 접근하는 비용은 커진다. 지연 초기화를 잘못사용하면 실제로 성능이 더 느려질 수도 있다.
멀티 스레드 환경에서 지연 초기화를 하기에는 까다롭다. 지연 초기화하는 필드를 둘 이상 스레드가 공유한다면 동기화해야 한다. 일반적으로 일반적인 초기화가 지연 초기화보다 낫다.
1
2
// 인스턴스 필드를 초기화하는 일반적인 방법
privatefinalFieldTypefield=computerFieldValue();
1
2
3
4
5
6
7
8
9
// 지연 초기화 - sychronized 방식
privateFieldTypefield;privatesychronizedFieldTypegetField(){if(field==null){field=computerFieldValue();}returnfield;}
1
2
3
4
5
6
7
8
// 정적 필드용 지연 초기화 홀더 클래스 관용구
privatestaticclassFieldHolder{staticfinalFieldTypefield=computerFieldValue();}privatestaticFieldTypegetField(){returnFieldHolder.field();}
getField가 호출되는 순간 FieldHolder.field가 처음 읽히면서 FieldHolder 클래스 초기화를 촉발한다. 일반적인 VM은 클래스를 초기화 할때만 필드 접근을 동기화할 것이다. 클래스 초기화가 끝난 후에는 VM이 동기화 코드를 제거하여, 그 다음부터는 아무런 검사나 동기화 없이 필드에 접근하게 된다.
성능 때문에 인스턴스 필드를 지연 초기화 한다면 이중검사 관용구를 사용하라. 한 번은 동기화 없이 검사하고 두 번째는 동기화하여 검사한다. 두 번째 검사에서 필드가 초기화되지 않았을 때만 초기화한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
privatevolatileFieldTypefieldType;privateFieldTypegetFieldType(){FieldTyperesult=fieldType;if(result!=null){// 첫 번째 검사 (락 사용 x)
returnresult;}synchronized(this){if(fieldType==null){// 두 번째 검사 (락 사용)
fieldType=computerFieldValue();}returnfieldType;}}