[JAVA]디자인패턴-4.Singleton

Singleton 패턴

싱글톤 패턴이란 인스턴스를 하나만 만들어 사용하기 위한 패턴이다. 커넥션 풀, 스레드 풀, 디바이스 설정 객체 등의 경우, 인스턴스를 여러 개 만들게 되면 자원을 낭비하게 되거나 버그를 발생시킬 수 있으므로 오직 하나만 생성하고 그 인스턴스를 사용하도록 하는 것이 이 패턴의 목적이다.
하나의 인스턴스만을 유지하기 위해 인스턴스 생성에 특별한 제약을 걸어둬야 한다. new를 실행할 수 없도록 생성자에 private 접근 제어자를 지정하고, 유일한 단일 객체를 반환할 수 있도록 정적 메소드를 지원해야 한다. 또한 유일한 단일 객체를 참조할 정적 참조변수가 필요하다.

예제

1
2
3
4
5
6
7
8
public class Singleton {
private static Singleton singletonObject = new Singleton();
public static Singleton getInstance(){
return singletonObject;
}
private Singleton(){
}
}

클래스 로드시 new가 실행되고 하나의 인스턴스를 가지게 된다. 코드도 쉽고 성능도 나름 좋은 편이다.
하지만 이렇게 되면 이 인스턴스를 사용하지 않을 경우에도 객체가 프로그램이 실행되서부터 끝까지 메모리에 존재하게 된다.

1
2
3
4
5
6
7
8
9
10
11
12
public class Singleton {
private static Singleton singletonObject;

private Singleton() {}

public static Singleton getInstance() {
if (singletonObject == null) {
singletonObject = new Singleton();
}
return singletonObject;
}
}

가장 기본적인 싱글톤 패턴이다.
클래스 로드시에 인스턴스가 생성되지 않고 getInstance 를 호출할 때 생성된다.
인스턴스를 사용할 필요가 없는 경우에는 생성되지 않는다는 장점이 있다.
하지만 멀티스레드 환경에서 동시에 접근하다 인스턴스가 두개가 생기는 문제가 발생할 여지가있다.

1
2
3
4
5
6
7
8
9
10
11
12
public class Singleton {
private static Singleton singletonObject;

private Singleton() {}

public static synchronized Singleton getInstance() {
if (singletonObject == null) {
singletonObject = new Singleton();
}
return singletonObject;
}
}

synchronized 키워드를 사용하여 동기화를 하였지만 성능상 문제가 있어 좋은 방법은 아니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Singleton {
private static volatile Singleton singletonObject;

private Singleton() {}

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

DCL(Double Checking Locking)을 써서 getSingletonObject()에서 동기화 되는 영역을 줄일 수 있다. 초기에 객체를 생성하지 않으면서도 동기화하는 부분을 작게 만들었다. 그러나 이 코드는 멀티코어 환경에서 동작할 때, 하나의 CPU를 제외하고는 다른 CPU가 lock이 걸리게 된다. 그렇기 때문에 다른 방법이 필요하다.

1
2
3
4
5
6
7
8
9
10
public class Singleton {
private Singleton(){
}
private static class SingletonHolder{
static final Singleton4 single = new Singleton4();
}
public static Singleton getInstatnce(){
return SingletonHolder.single;
}
}

마지막 방법은 내부 클래스를 이용하는 방법이다. 내부 클래스가 호출되는 시점에 최초로 생성이 되기 때문에 필요하지 않을때 생성하지 않을 수 있으며 속도도 빠르다.