구현

[구현] 싱글톤(Singleton) 패턴 직접 적용해보기

키태 2023. 12. 6. 11:00
728x90

회사 프로젝트 중 싱글톤 패턴을 활용해 코드를 짜고 있었다. 스프링에서는 기본적으로 싱글톤 패턴을 제공해주고 있지만 직접 구현하고 쓰는 건 처음이다. 싱글톤 패턴이란 객체의 인스턴스가 오직 1개만 생성되는 패턴을 의미하며 이런 정의는 모두나 알고 있지만 구체적으로 어떻게 쓰이는지, 또 여러 방식으로 쓰이지만 각 방식에 있어서 장단점이 무엇인지 알게된 뿌듯한 시간이었다.

 

 

1. Eager Initialization

우선 가장 간단한 방식으로 Eager Initialization 방식이 있다. 코드부터 보도록 하자

public class VoteUtil {
	private static VoteUtil instance = new VoteUtil();
	private VoteUtil() {}
	public static VoteUtil getInstance() {
		return instance;	
	}
}

 

 

우선 static으로 VoteUtil 인스턴스를 선언한 것을 볼 수 있다. 위 방식대로 하면 클래스 로더에 의해 클래스가 로딩될 때 싱글톤 객체가 생성된다. static으로 클래스가 최초 로딩될 때 객체가 생성되므로 Thread-Safe 하다는 장점이 있다. 

하지만, 싱글톤 객체의 사용유무와 관계없이 클래스가 로딩되는 시점에 항상 싱글톤 객체가 생성되고, 메모리를 잡고 있기 때문에 비효율적일 수 있다.

 

2. Lazy Initialization

Eager Initialization 방식에 단점을 보완하고자 Lazy Initialization 방식을 도입했다.

public class VoteUtil {
	private static VoteUtil instance;
	private VoteUtil(){}
	public static LazyInitialization getInstance() {
		if(instance == null) {
			instance = new VoteUtil();
		}
		return instance;
	}
}

 

 

이렇게 싱글톤 패턴을 적용하게 되면, 클래스가 로딩되는 시점이 아닌 클래스의 인스턴스가 사용되는 시점에 싱글톤 인스턴스를 생성한다. 즉 사용시점까지 싱글톤 객체 생성을 미루기 때문에 사용하기 전까지 메모리를 점유하지 않는다. 하지만 instance == null로 중복 생성을 막았지만 multi-thread 환경에서 두번 생성될 여지가 있다. 즉, multi-thread 환경에서는 싱글톤 철학이 깨질 수 있는 위험이 있다는 단점이 있다.

 

3. Thread Safe Lazy Initialization

Lazy Initialization 방식에서 thread-safe 하지 않다는 단점을 보완하기 위해 synchronized 키워드를 도입했다.

public class VoteUtil {
	private static VoteUtil instance;
	private VoteUtil(){}
	public static synchronized LazyInitialization getInstance() {
		if(instance == null) {
			instance = new VoteUtil();
		}
		return instance;
	}
}

 

synchronized 키워드를 사용할 경우, 자바 내부적으로 해당 영역이나 메소드를 lock, unlock 처리하기 때문에 thread-safe 하다는 장점이 있지만 이 과정에서 성능저하가 발생한다.

 

4. Initialization on demand holder idiom

public class VoteUtil {
	private VoteUtil(){}
	private static class InnerInstanceClass {
		private static final VoteUtil instance = new VoteUtil();
	}
	public static VoteUtil getInstance() {
		return InnerInstanceClass.instance;
	}
}

 

VoteUtil 안의 innerClass InnerInstanceClass를 두면, getInstance() 메소드가 호출되기 전까지는 참조되지 않으며 메소드가 호출되면 static으로 선언된 instance를 한번만 호출하고 final을 통해 다시 값이 할당되지 않도록 한다. 즉 위의 모든 단점을 보완한 방법이다.

 

이렇게 싱글톤(Singleton) 패턴에 대해서 알아봤다. 각 방식이 어떻게해서 도입된 방식이고 장단점을 분석해보며 한층 이해도가 깊어진 느낌:)

728x90