아래 코드는 느린 초기화로 불리는 Lazy Initialization, 전 글에서 보았던 전형적인 싱글턴 구현법이다.

여러 스레드에서 getInstance() 를 호출하고 동시에 if 문을 타게 되다면 null이라 판단하여 인스턴스가 두 번 생성된다.

public class Singleton {
	private static Singleton unique;
 
	private Singleton() {
	}
 
	public static Singleton getInstance() {
		if (unique == null) {
			unique = new Singleton();
		}
		return unique;
	}
}

멀티 스레드 환경을 만들어 간단히 동시성 문제를 발생시켜 보았다.

동시성 문제 발생

 

동시성 문제 해결법은??

1. synchronized 키워드 추가

public class Singleton {
	private static Singleton unique;
 
	private Singleton() {
	}
 
	public static synchronized Singleton getInstance() {
		if (unique == null) {
			unique = new Singleton();
		}
		return unique;
	}
}

synchronized 는?

  • 스레드간 동기화를 시켜 thread-safe 하게 해줌
  • 여러개의 스레드가 하나의 자원을 사용하고자 할 때
  • 남발하면 성능저하

 

2. DCL (Double Checked Locking)

두번 체크! synchronized 위치를 바꾸면서 동기화 오버헤드를 낮춤

public class Singleton {
	private static Singleton unique;

	private Singleton() {
	}

	public static Singleton getInstance() {
		if (unique == null) {
			synchronized (Singleton.class) {
				if (unique == null) {
					unique = new Singleton();
				}
			}
		}
		return unique;
	}
}

 

인텔리제이에선 DCL 을 바로 파악하고 holder 방법으로 권유한다.

 

3. Holder

클래스 로딩 단계에서 전역 인스턴스를 생성하지 않는 장점

getInstance() 호출 시 생성된다. 많은 블로그를 보았는데 다 이 방식을 추천한다.

public class Singleton {

	private Singleton() {
	}

	public static Singleton getInstance() {
		return Holder.instance;
	}

	private static class Holder {
		public static final Singleton instance = new Singleton();
	}
}
복사했습니다!