You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
지연 초기화(Lazy Initialization): 필드의 초기화 시점을 그 값이 처음 필요할 때까지 늦추는 기법
값이 쓰이지 않으면 초기화도 일어나지 않는다
정적 필드, 인스턴스 필드 모두에 사용 가능하다
주로 최적화 용도로 쓰이고, 클래스와 인스턴스 초기화 때 발생하는 순환 문제를 해결하기도 한다
지연 초기화, 필요할 때까지는 하지 말라
❗️지연 초기화는 양날의 검이다
클래스/인스턴스 생성 시의 초기화 비용은 줄지만, 지연 초기화하는 필드에 접근하는 비용은 커진다
사용하면 좋은 경우: 해당 클래스의 인스턴스 중 그 필드를 사용하는 인스턴스의 비율은 낮은데, 필드를 초기화하는 비용이 클 경우
위의 경우인지 알기 위해서는 지연 초기화 적용 전후의 성능을 측정해봐야한다
멀티스레드 환경
지연 초기화하는 필드를 둘 이상의 스레드가 공유하면 어떤 형태로든 동기화해야 한다.
일단, 대부분의 상황에서 일반적인 초기화가 지연 초기화보다 낫다
privatefinalFieldTypefield = computeFieldValue();
지연 초기화가 반드시 필요한 상황이라고 판단된다면 다음 기법들을 사용할 수 있다. 아래 기법들은 기본 타입 필드와 객체 참조 필드 모두에 적용할 수 있다.
Thread Safe 한 지연 초기화 패턴
1. 지연 초기화를 초기화 순환성(initialization circulatirty)을 깨뜨리는데 사용하는 경우
synchronized를 단 접근자를 사용하자
정적 필드도 필드와 메소드에 static만 추가하면 똑같은 패턴을 쓸 수 있다
privateFieldTypefield;
// synchronized를 사용해 thread safe하게 메소드를 사용할 수 있다// 필드가 초기화되지 않은 경우만 computeFieldValue()로 초기화한다privatesynchronizedFieldTypegetField() {
if (field == null)
field = computeFieldValue();
returnfield;
}
privatestaticclassFieldHolder {
staticfinalFieldTypefield = computeFieldValue();
}
// 클래스는 클래스가 처음 쓰일 때 초기화된다!// getField()가 처음 호출되는 순간 FieldHolder.field가 처음 읽히며, FieldHolder 클래스 초기화가 된다privatestaticFieldtypegetField() {
returnFieldHolder.field;
}
getField 메서드가 필드에 접근하면서 동기화를 하지 않아, 성능이 느려지지 않는다는 장점이 있다
(일반적인 VM은 클래스를 초기화할 때만 필드 접근을 동기화하고, 끝난 후에는 동기화 코드를 제거하여 그 다음부터는 검사나 동기화 없이 필드에 접근한다)
3. 성능 때문에 인스턴스 필드를 지연 초기화해야 할 경우
이중검사(double-check) 패턴을 사용하라
필드의 값을 두 번 검사하는데, 필드가 아직 초기화되지 않았다면 동기화 없이 검사하고, 두 번째는 동기화하여 검사한다. 두 번째 검사에서도 필드가 초기화되지 않았을 때만 필드를 초기화환다.
초기화된 필드에 접근할 때의 동기화 비용을 없애준다
수치 기본 타입 필드에 적용하면 null 대신 0과 비교
// 필드가 초기화된 후에는 동기화하지 않으므로 volatile 선언privatevolatileFieldTypefield;
privateFieldTypegetField() {
// result 지역변수를 사용하면 필드가 이미 초기화된 상황에서는 필드를 딱 한 번만 읽도록 보장할 수 있다FieldTyperesult = field;
// 첫번째 검사 (동기화 없이)if (result != null) {
returnresult;
}
// 두번째 검사 (동기화 사용)synchronized(this) {
if (field == null) {
field = computeFieldValue();
}
returnfield;
}
}
4. 반복해서 초기화해도 상관없는 인스턴스 필드를 지연 초기화해야 할 경우
단일 검사 패턴(single-check)
3번에서 두 번째 검사를 생략할 수 있다.
// 필드가 초기화된 후에는 동기화하지 않으므로 volatile 선언privatevolatileFieldTypefield;
privateFieldTypegetField() {
FieldTyperesult = field;
if (result == null) {
field = result = computeFieldValue();
}
returnresult;
}
5. 모든 스레드가 필드의 값을 다시 계산해도 상관없고 필드의 타입이 long과 double을 제외한 다른 기본 타입일 경우
volatile 한정자를 없애도 된다
racy single-check 패턴이라고 불린다
어떤 환경에서는 필드 접근 속도를 높여주지만 초기화가 스레드당 최대 한 번 더 이루어질 수 있다. 보통은 거의 쓰지 않는 패턴이다.
핵심 정리
대부분의 필드는 지연시키지 말고 곧바로 초기화하라
성능 혹은 초기화 순환을 막기 위해 지연 초기화를 써야 한다면, 위에서 설명한 경우에 따라 올바른 패턴을 사용하자
The text was updated successfully, but these errors were encountered:
SooKim1110
changed the title
# [ Item83 ] 지연 초기화는 신중히 사용하라
[ Item83 ] 지연 초기화는 신중히 사용하라
Jun 25, 2022
[ Item83 ] 지연 초기화는 신중히 사용하라
지연 초기화
지연 초기화(Lazy Initialization): 필드의 초기화 시점을 그 값이 처음 필요할 때까지 늦추는 기법
지연 초기화, 필요할 때까지는 하지 말라
❗️지연 초기화는 양날의 검이다
멀티스레드 환경
지연 초기화하는 필드를 둘 이상의 스레드가 공유하면 어떤 형태로든 동기화해야 한다.
일단, 대부분의 상황에서 일반적인 초기화가 지연 초기화보다 낫다
지연 초기화가 반드시 필요한 상황이라고 판단된다면 다음 기법들을 사용할 수 있다. 아래 기법들은 기본 타입 필드와 객체 참조 필드 모두에 적용할 수 있다.
Thread Safe 한 지연 초기화 패턴
1. 지연 초기화를 초기화 순환성(initialization circulatirty)을 깨뜨리는데 사용하는 경우
2. 성능을 위해 정적 필드를 지연 초기화해야 할 경우
(일반적인 VM은 클래스를 초기화할 때만 필드 접근을 동기화하고, 끝난 후에는 동기화 코드를 제거하여 그 다음부터는 검사나 동기화 없이 필드에 접근한다)
3. 성능 때문에 인스턴스 필드를 지연 초기화해야 할 경우
4. 반복해서 초기화해도 상관없는 인스턴스 필드를 지연 초기화해야 할 경우
5. 모든 스레드가 필드의 값을 다시 계산해도 상관없고 필드의 타입이 long과 double을 제외한 다른 기본 타입일 경우
핵심 정리
The text was updated successfully, but these errors were encountered: