책을 읽다가 유용한 정보를 발견하여 남깁니다.

 Head First Design Patterns 中에서..

싱글턴을 모르시는 분과 이미 알고 계시는 분들에게 큰 도움이 되리라 생각합니다.

 

싱글턴이란?

싱글턴 패턴은 해당 클래스의 인스턴스가 하나만 만들어지고, 어디서든지 그 인스턴스에

접근할 수 있도록 하기 위한 패턴입니다.

 

일반적인 싱글턴 패턴 구현법

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

일반적으로 2개 이상의 객체가 만들어지면 안되는 클래스에서 많이 사용하는데

Singleton.getInstance() 이런식으로 접근을 하죠.

그럼 다른 객체에서 언제든지 인스턴스를 얻어와서 사용할 수 있게 됩니다.

 

하지만 이 코드에는 문제점이 있습니다.

싱글턴은 유일무이한 객체는 위한 패턴이지만

2개 이상의 다중스레드에서 접근하게 될 경우, 인스턴스가 중복 생성될 수 있습니다.

 

이걸 해결하기 위한 방법은 간단합니다.

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

 

synchronized 키워드를 붙여 동기화 시켜주면 됩니다.


 

하지만 이것도 함정입니다.

동기화가 꼭 필요한 시점은 인스턴스가 생성될때입니다.

일단 uniqueInstance 변수에 Singleton 인스턴스를 대입하고 나면

더이상 동기화된 상태를 유지시킬 필요가 없는거죠.

처음 과정만 제외하면 불필요한 오버헤드만 증가시킬 뿐입니다.

 

이를 해결하기 위한 몇가지 방법이 있습니다.

 

1. 해당 싱글턴 객체의 속도가 그리 중요하지 않으면 그냥 둡니다.

- 어플에 큰 부담을 주지 않으면 그냥 놔둬도 됩니다.

하지만 동기화를 하면 성능이 100배 정도 저하된다는 것은 기억해야 합니다.

만약 해당 객체가 어플에 중요하게 작용한다면 다른 방법을 생각해야 합니다.

 

2. 인스턴스를 필요할 때 생성하지 말고, 처음부터 만들어 버립니다.

- 어플에서 반드시 Singleton의 인스턴스를 생성하는 것도 괜찮은 방법입니다.

 

3. DCL(Double-Checking Locking) 을 써서 동기화되는 부분을 줄입니다.

- DCL을 사용하면, 일단 인스턴스가 생성되어 있는지 확인한 다음, 생성되어 있지 않았을 때만

동기화를 할 수 있습니다. 이렇게 하면 처음에만 동기화를 하고 나중에는 동기화를  하지 않아도 됩니다.

가장 적합한 방법입니다.

 

uniqueInstance에 volatile 키워드를 붙여 DCL을 사용할 수 있습니다.

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