2. 멀티 쓰레드 환경에서 안전하게 구현하는 방법

방법 : Double checked locking, Inner Class

멀티 스레드 환경에서는 이 방법은 thread safe하지 않는다고 한다.

만약 2개의 쓰레드가 존재할 때, 1번 스레드가 if문 안으로 접근하게 된다면 아직 인스턴스를 생성하지 않은 시점에서 2번 스레드도 if문 안으로 진입하게 되는 상황이 있다고 하자.

  • 1, 2번이 가지게되는 인스턴스가 달라지게 된다.

가장 쉬운 방법 중 하나는 메서드를 동기화 시키는 것이다. synchronized키워드를 사용한다.

  • 적용된 메서드에서 한번에 딱 하나의 스레드만 들어오게끔 만드는, 동시에 여러 쓰레드가 이 메서드 안으로 들어올 수 없기 때문에, 멀티 스레드 환경에서 하나의 인스턴스만 보장할 수 있다.

    • 단점 : getInstance()를 호출할 때마다 동기화 처리하는 작업 때문에 성능 이슈가 생길 수 있다.

만약 synchronized를 사용하지 않으려면 미리 static final로 인스턴스를 생성한다.

  • 이른 초기화(Eager Initialization) 사용

  • private static final Settings INSTACE = new Settings();

    • 이 클래스는 애플리케이션이 로딩되는 시점에 satic 필드들이 초기화 된다.

    • 이 방법은 thread safe하다.

  • 단점 : 미리 만든다는 자체가 단점이 될 수 있다.

    • 인스턴스를 만드는 과정이 길고 비용이 많이 발생한다면, 만들어놨는데 사용하질 않아?? 그럼 애플리케이션 로딩할 때 많은 리소스를 사용했음에도 불구하고 안쓰는 객체를 생성한 것이다.

그럼 어떤 방법이 있을까?

Double checked locking 사용하기 —> 체크를 두번하기


그럼 나는 인스턴스를 나중에 사용이 될 때 만들고 싶은데 synchronized 블럭의 비용이 신경이 쓰이는데…. 어떻게하면 좋겠는가…?

먼저, if문으로 체크 하고나서, 분기 안으로 들어오면 synchronized블럭을 생성해서 Setting 클래스를 lock으로 사용하고, 그 안에서 한번 더 검사를 진행하고 인스턴스를 생성한다.

  • volatile 키워드 사용

    • 장점 : 메소드 레벨에서 synchronized가 있는것에 비하면 성능에 유리하다.

    • 단점 : 매우 복잡한 방법, 왜 volaatile을 쓰는지 이해해야 하고, java 1.5이상 부터 동작한다.

좀 더 심플하게 만드는 법 - inner 클래스 사용 : 권장하는 방법 중 하나


getInstance()를 통해서 inner class에서 생성한 인스턴스를 리턴하게 됨

이 방법은 멀티 스레드 환경에서도 안전하고, getInstance()가 호출될 때 SettingsHolder 클래스가 로딩이 되고, 그 때 Settings 인스턴스를 생성하기 때문에 Lazy Loading이 가능한 코드가 된다.

Last updated