싱글톤(singleton)은 '단독 개체', '독신자'라는 뜻 말고도 '정확히 하나의 요소만 갖는 집합' 등의 의미가 있다. singleton 패턴은 객체의 생성과 관련된 패턴으로서 특정 클래스의 객체가 오직 한 개만 존재하도록 보장한다. 즉 클래스의 객체를 하나로 제한한다. 프로그램에서 이런 개념이 필요할 때는 언제일까? 프린터 드라이버의 예를 들어보자.

 

여러 컴퓨터에서 프린터 한 대를 공유하는 경우, 한 대의 컴퓨터에서 프린트하고 있을 때 다른 컴퓨터가 프린트 명령을 내려도 현재 프린트하는 작업을 마치고 그다음 프린트를 해야지 두 작업이 섞여 나오면 문제가 될 것이다. 즉 여러 클라이언트(컴퓨터)가 동일 객체(공유 프린터)를 사용하지만 한 개의 객체(프린트 명령을 받은 출력물)가 유일하도록 상위 객체가 보장하지 못한다면 singleton 패턴을 적용해야 한다. 이처럼 동일한 자원이나 데이터를 처리하는 객체가 불필요하게 여러 개 만들어질 필요가 없는 경우에 주로 사용한다.

 

사실 싱글톤을 구현하는 방법은 몇 가지 존재한다. 하지만 오늘 소개할 싱글톤 패턴 코드는 가장 많이 사용하고 안전한 싱글톤 객체를 만드는 코드 한가지만 다루어 볼것이다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Singleton {
    
    private Singleton() {}
    
    private static class LazyHolder{
        public static final Singleton SINGLETON=new Singleton();
    }
    
    public static Singleton getInstance() {
        return LazyHolder.SINGLETON;
    }
    
}
cs

 

위 코드는 싱글톤으로 생성될 객체를 Lazy Loading 하는 코드이다. 클래스가 로딩될때 LazyHolder라는 내부 클래스는 로딩되지 않는다. 이유는 LazyHolder라는 객체를 참조하여 호출하는 코드가 존재하지 않기 때문이다. 그 말은 진짜 Singleton 객체가 필요하여 getInstance()를 호출할때 LazyHolder 클래스가 로딩되는 것이다. 바로 필요할때 객체를 생성한다는 장점이 있다.

 

또 하나의 장점이 있다. 해당 코드는 다른 싱글톤 생성 코드와 달리 싱글톤 인스턴스가 NULL인지 체크하는 것과 synchronized 블럭을 사용하여 동기화하는 부분도 존재하지 않는다. 왜 일까? 그러면 과연 멀티 스레드 환경에서 안전할까? 안전하다! JVM에서 클래스를 로딩하는 과정에서는 멀티 스레드 환경에서도 안전한 동기화 환경을 제공하기 때문이다! 즉, LazyHolder 클래스가 로딩되는 과정에 static 한 SINGLETON 인스턴스를 생성하기 때문에 싱글톤 객체 생성과정에서 별도의 동기화 처리를 하지 않아도 클래스 로딩되는 과정 자체가 JVM에서 동기화 환경을 제공하기 때문에 멀티 스레드 환경에서도 안전한 것이다!

 

LazyHolder를 이용한 싱글톤 객체 생성 시 장점

  1. 싱글톤 객체가 진짜 필요할 때까지 초기화를 늦춘다.(Lazy Loading)
  2. JVM 클래스 로딩 타임때 싱글톤 객체를 생성하므로 멀티 스레드 환경에 안전한 동기화 이슈를 JVM이 직접 해결해준다.

싱글톤 객체를 생성할 때는 위의 방법을 이용하자!

 

 

기타 싱글톤 객체 생성 코드

나머지 방법들은 멀티 스레드에 안전하지 않으면서도 불필요한 Lock을 잡고 있는 방법들이기 때문에 사용을 권장하지 않는다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
public class Singleton_methodSync {
    
    private static Singleton_methodSync instance;
 
    private Singleton_methodSync() {}
    
    public static synchronized Singleton_methodSync getInstance() {
        if(instance == null) {
            instance = new Singleton_methodSync();
        }
        return instance;
    }
    
}
 
//Double Checked Lock
public class Singleton_methodSync2 {
    
    private static Singleton_methodSync2 instance;
    
    private Singleton_methodSync2() {}
    
    public static Singleton_methodSync2 getInstance() {
        if(instance == null) {
            synchronized (Singleton_methodSync2.class) {
                if(instance==null) instance = new Singleton_methodSync2();
            }
        }
        return instance;
    }
}
 
//이른 초기화
public class Singleton_precreate {
    
    private static Singleton_precreate instance = new Singleton_precreate();
    
    private Singleton_precreate() {    }
    
    public static Singleton_precreate getInstance() {
        return instance;
    }
    
}
 

public class Singleton_notsafety {        

private static Singleton_methodSync instance;

    private Singleton_methodSync() {}    

    public static Singleton_methodSync getInstance() {

        if(instance == null) {

            instance = new Singleton_methodSync();

        }

        return instance;

    }    

}

 cs
posted by 여성게
: