ch05_singletonpattern

Toggle Space Navigation Tree
Space Map

Singleton Pattern

싱글톤 패턴의 목적은 두가지

  1. 클래스의 인스턴스를 하나만 생성한다.
  2. 어디서든 해당 인스턴스에 접근할 수 있다.

왜 싱글톤이 필요한가?

  • 하나만 존재해야 좋은 것들이 있다.
    • 쓰레드 풀, 캐시, 설정...
  • 늦은 초기화( Lazy instantiation ) 를 가능케 한다. --> 이것이 전역변수의 단점
    • 하나의 객체를 만들지만 그 하나의 객체가 항상 필요하지 않을 수도 있다.
    • 때문에 늦은 초기화를 통해 사용하지 않는 경우 객체 생성에 소비되는 리소스를 아낄 수 있다.

기본적인 코드

// 싱글턴객체가 필요할 때는 인스턴스를 직접 만드는게 아니고 인스턴스를 달라고 요청을 하는 것이다.
public class Singleton {
  private static Singleton uniqueInstance;
	
  private Singleton() {}  // <---  생성자는 private -- 외부에서 new 를 사용하여 객체를 생성할 수 없다...
	
  public static Singleton getInstance() {  // 객체를 생성하는 static 메소드 하나
    if ( uniqueInstance == null ) {        // 이 부분은 객체를 처음 생성할 때만 필요하다~~
      uniqueInstance = new Singleton();
    }
	
    return uniqueInstance;
  }
}

Single Thread 인 경우 이 정도면 충분할 듯 합니다.

다음은 Multi Thread 환경인 경우에 해당되는 내용입니다.


기본 코드의 멀티 쓰레드에서의 문제점
public class Singleton {
  private static Singleton uniqueInstance;
	
  private Singleton() {}
	
  public static Singleton getInstance() {  
    if ( uniqueInstance == null ) {          // A Thread가 이 코드를 실행할 때
      uniqueInstance = new Singleton();      // B Thread가 객체 생성을 완료하지 않은 경우 
    }                                        // 두개의 객체가 생성될 수 있다.
	
    return uniqueInstance;
  }
}

두개의 객체가 생성될 수 있는 문제를 해결할 수 있는 간단한 방법은 아래와 같다.

수정된 코드
public class Singleton {
  private static Singleton uniqueInstance;
	
  private Singleton() {}

  // 메소드 자체에 synchronized 를 거는 방법	
  public static synchronized Singleton getInstance() {  
    if ( uniqueInstance == null ) {          
      uniqueInstance = new Singleton(); 
    }                                        
	
    return uniqueInstance;
  }
}
    
  • 이 방법은 성능에 영향을 끼칠 수 있다.
    ( 메소드를 동기화하면 성능이 100배 정도 저하된다고 책에 적혀있습니다. )
  • synchronized가 필요한 경우는 객체가 생성되는 처음 뿐이고 그 이후에는 쓸데없이 성능을 저하시키는 요인이다.
  • 이 코드를 개선한 것이 아래의 DCL ~~
public class Singleton {
  private static Singleton uniqueInstance = new Singleton();
  private Singleton() {}
 
  public static Singleton getInstance() {
    return uniqueInstance;
  }
}
    
  • 객체의 사용여부와 상관없이 생성되는 전역변수 스타일~~
Double-Checking Locking
public class Singleton {
  private volatile static Singleton uniqueInstance;
  
  private Singleton() {}

  // 부분적으로 synchronized를 걸어 객체가 생성된 이후에는 synchronized 구문을 수행하지 않는다.
  public static Singleton getInstance() {
    if ( uniqueInstance == null ) {
      synchronized( Singleton.class ) {
        if ( uniqueInstance == null ) {
          uniqueInstance = new Singleton();
        }
      }
    }
    return uniqueInstance;
  }
}

- 꼭 volatile 키워드가 지정되어야 하고 자바 1.4 이후 버젼에서만( Tiger 부터~~ ) 사용해야 한다. 
volatile

http://blog.naver.com/amebas?Redirect=Log&logNo=120022290373

volatile은 하나의 변수를 여러 쓰레드에서 사용할 때, 사용하는 키워드입니다.

한 변수를 여러 쓰레드에서 사용하게 되면 각 쓰레드마다 해당 변수 값을 저장하는 작업 복사본을 하나씩 가지고
있습니다. 실제 쓰레드는 주 원본에서 값을 접근하기 위해 자신의 작업 복사본에 값을 저장하고 어떠한 연산을
거친뒤에 다시 원본 값을 돌려버리는 방식을 취하죠...

이럴경우 동기화에 문제가 있습니다... 만약 화면에 변수 값을 찍는 작업을 위해 하나의 쓰레드가 자신의 작업
복사본에 값을 읽어들어왔습니다... 그리고 그 값을 찍기 이전에 다른 쓰레드가 그것을 변경했다고 한다면,
한 쪽 쓰레드는 변경되기 이전의 값이 존재하고 다른 한 쪽은 변경된 값을 가지게 됩니다...

이것을 막기 위해 volatile을 사용하면 쓰레드가 특정 변수를 접근하려는 연산이 발생하면 무조껀 다시 주 원본의
값을 가져오게 하는 것이죠...

...
...
...

더 깊이 있는 내용을 원한다면...

쓰레드에 안전한 싱글톤 클래스를 만드는 세가지 방법 http://www.javaservice.net/~java/bbs/read.cgi?m=devtip&b=javatip&c=r_p&n=1028393658

Double-checked locking과 Singleton 패턴 http://www.javaservice.net/~java/bbs/read.cgi?m=devtip&b=javatip&c=r_p&n=1036509257

Enter labels to add to this page:
Please wait 
Looking for a label? Just start typing.