peaches-book-study / effective-java

이펙티브 자바 3/E
0 stars 2 forks source link

Item 83. 지연 초기화는 신중히 사용하라 #73

Open jseok0917 opened 1 month ago

jseok0917 commented 1 month ago

Chapter : 11. 동시성

Item : 83. 지연 초기화는 신중히 사용하라

Assignee : jseok0917


🍑 서론

지연 초기화(Lazy initialization)


일반적인 초기화 vs 지연 초기화



🍑 본론

지연 초기화는 어떤 상황에 써야할까?

  1. 성능 최적화를 위해

    • 필드의 초기화 비용이 크고, 필드를 낮은 빈도로 사용할 때
  2. 초기화 순환성을 피하기 위해 (아래 번외 참고, 책 443페이지에 번역오류있음, https://github.com/JavaBookStudy/JavaBook/issues/45 참고)



어떻게 지연 초기화 코드를 짜야할까?

  1. 멀티스레드 환경을 고려
public class LazyInitializationExample {
    private Resource resource; //선언만 해두고

    //getter를 통해 실제로 해당 필드를 가져올 때 값이 null이면 할당해준다
    //동시성을 고려하여 Synchronized 키워드로 작성
    public synchronized Resource getResource() { 
        if (resource == null) {
            resource = new Resource();
        }
        return resource;
    }
}


  1. 지연 초기화 홀더 클래스 관용구 사용
public class LazyInitializationExample {

    //정적 내부 클래스 사용
    private static class ResourceHolder {
        static final Resource INSTANCE = new Resource();
    }

    public Resource getResource() {
        return ResourceHolder.INSTANCE;
    }
}


  1. 혹은 이중검사 관용구를 사용
public class LazyInitializationExample {
    private volatile Resource resource; //선언만 해두고, volatile로!

    //getter를 통해 실제로 해당 필드를 가져올 때 값이 null이면 할당해준다
    //Synchronized 키워드를 메서드 내부 검사로직에 넣어준다
    public Resource getResource() { 
        Resource result = this.resource;

        //값이 이미 할당돼있는 상태면 바로 결과 반환(lock 사용X)
        if (result != null) {
            return result;
        }

        //초기화가 안돼있을때만 lock 사용
        synchronized(this) {
            if (result == null) {
                result = new Resource();
            }
            return result;
        }
    }
}



🍑 결론


번외

초기화 순환성이란?

  1. StackOverFlow
    public static class A {
        private B b;

        // A 객체 생성 시 A 인스턴스를 새로 만들기 때문에 문제가 발생한다.
        public A() {
            b = new B(new A());  // 새로운 A 인스턴스를 생성하며, 이로 인해 무한 루프가 발생
        }
    }

    public static class B {
        private A a;

        public B(A a) {
            this.a = a;
        }
    }

    public static void main(String[] args) {
       A a = new A();  // 무한 루프에 빠짐
    }


public static class A {
    private B b;

    public A() {
        // 지연 초기화를 위해 B 인스턴스 생성을 미룸
    }

    //두번 초기화되는 문제를 해결하기 위해 synchronized
    public synchronized B getB() {
        if (b == null) {
            b = new B(this); // 필요 시에만 B 인스턴스 생성
        }
        return b;
    }
}

public static class B {
    private A a;

    public B(A a) {
        this.a = a;
    }
}

public static void main(String[] args) {
    A a = new A();  // 무한 루프에 빠지지 않음
    B b = a.getB(); // B 인스턴스가 처음 사용될 때 생성
}

Referenced by