- `getField()` 메서드가 호출되면, `FieldHolder.field` 가 읽히면서 `FieldHolder` 클래스 초기화를 한다
- 동기화를 하지 않기 때문에 성능이 느려질 걱정이 없다는 장점을 가진다
### 4. 인스턴스 필드 지연 초기화 이중검사 관용구
- 성능 때문에 인스턴스 필드를 지연 초기화해야 한다면, 이중검사 관용구를 사용하자
- 초기화된 필드에 접근할 때의 동기화 비용을 없애준다
```java
private volatile FieldType field;
private FieldType getField() {
FieldType result = field;
// 첫번째 검사 (Lock 사용 안함)
if (result != null) {
return result;
}
// 두번째 검사 (Lock 사용)
synchronized(this) {
if (field == null) {
field = computeFieldValue();
}
return field;
}
}
한 번은 동기화 없이 검사하고, 두 번째 검사는 동기화하여 검사한다
두번째 검사에서도 필드가 초기화되지 않았을 때만 필드를 초기화한다
필드가 초기화 된 후로는 동기화하지 않으므로 해당 필드는 반드시 volatile로 선언해야 한다
volatile
java 변수를 메인 메모리에 저장하겠다고 명시하는 키워드
매번 변수의 값을 read 할 때 마다 CPU cache에 저장된 값이 아닌, 메인 메모리에서 읽는다
변수의 값을 write할 때 마다 메인 메모리에까지 작성한다
volatile 변수를 사용하지 않는 멀티스레드 어플리케이션에서는 태스크를 수행하는 동안 성능 향상을 위해 메인 메모리에서 읽은 변수 값을 CPU cache 에 저장한다
만약, 멀티스레드 환경에서 스레드가 변수 값을 읽어올 때, 각 CPU cache에 저장된 값이 다르기 때문에 변수 값 불일치 문제가 발생하게 된다
volatile 키워드를 통해 멀티 스레드 환경에서 하나의 스레드만 read & write하고, 나머지 스레드가 read하는 상황에서 가장 최신의 값을 보장한다
CPU cache보다 메인 메모리가 비용이 더 크기 때문에 변수 값 일치를 보장해야 하는 경우에만 volatile을 사용하는 것이 좋다
5. 이중검사 관용구의 변종 - 단일 검사 관용구
이중 검사 관용구에서 반복해서 초기화해도 상관없는 인스턴스 필드를 지연 초기화할 때 초기화가 중복해서 일어날 수 있다
private volatile FieldType field;
private FieldType getField() {
FieldType result = field;
if (result == null) {
field = result = computeFieldValue();
}
return result;
}
### 6. 이중검사 관용구의 변종 - 짜릿한 단일 검사 관용구 (racy single-check)
- 모든 스레드가 필드의 값을 다시 계산해도 상관없고, 필드의 타입이 long과 double을 제외한 다른 기본 타입이라면, 필드 선업에서 `volatile` 한정자를 없애도 된다
```java
private FieldType field;
private FieldType getField() {
FieldType result = field;
if (result == null) {
field = result = computerFieldValue();
}
return result;
}
이 관용구는 필드의 접근 속도를 높여주지만, 초기화가 스레드 당 최대 한 번 더 이뤄질 수 있다
[83] 지연 초기화는 신중히 사용하라
지연 초기화
지연 초기화 특징
초기화 방법
1. 일반적인 초기화 방법
2. 지연 초기화 - synchronized 한정자
synchronized
를 단 접근자를 사용해라3. 정적 필드 지연 초기화 홀더 클래스 관용구
private static FieldType getField() { return FieldHolder.field; }
volatile
로 선언해야 한다5. 이중검사 관용구의 변종 - 단일 검사 관용구
private FieldType getField() { FieldType result = field;
}