책을 읽다가 유용한 정보를 발견하여 남깁니다.
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; } ....... ..... }
최고의 자료는 여기죠..
http://www.ibm.com/developerworks/kr/library/j-dcl.html
한역되었군요. 정말 머리속에 쏙쏙 들어오네요. 감사합니다.
그냥 다시 모아서 찾다보니, ibm 원문이 2002년이라서 Java 5 나온 2004년 시점의
시각이 완전하게 반영된게 아닌거 같습니다.
전 예전에 선언문을 인상깊게 봤는데요.
The "Double-Checked Locking is Broken" Declaration
IBM 문서 좋아서 종종 찾아보고 있습니다.
문서가 오래 되어서 과거 내용을 다루고 있었는데 바로 지적해주시는군요 ㅎ
위에 내용에 대해서는
멀티스레드 상황에서 동시에 인스턴스 생성이 된다면 문제가 되겠지요
Static Class , Singleton Pattern 모두 처음 Instance 되는 시점은 멀티 스테드가
동시에 접근되지 않게 만들어야 하지 않나 생각합니다.
그리고 제일 중요한건.. 메모리를 크게 잡아 먹는 Class를 싱글톤으로 만들어 버리면
메모리릭이 발생될 수 있으므로
싱글톤 내부에 릴리즈를 반듯이 구현해야 될 거 같고요..
세심한 사용이 필요한 부분이 많은거 같네요..
글 잘 봤습니다.
싱글톤 패턴은 프로젝트 전반에 걸쳐 자주 사용되는 가장 핵심적인 요소에만 사용하는게 좋다는 생각입니다.
그렇지 않은 클래스는 굳이 싱글톤을 쓸 필요가 없습니다.
그런 생각에 저는 2번 방법이 싱글톤에 가장 적합한 방법이라고 생각합니다. ^^
왜냐면, 한번 생성된 싱글톤은 static하게 남아있는데 프로젝트 전반에 걸쳐 사용되는 클래스를
굳이 인스턴스를 최초에 얻을때 생성하는 것도 별로 좋은 모양새는 아니에요.
사소한 차이지만 잦은 instance 참조시에도 성능이슈가 거의 전무하다는 것도 장점이겠죠.
volatile자체는 DCL 과 아무 관련 없습니다.
volatile의 의미는 해당된 변수와 관련된 메모리 작업을 optimizing 하지 말것을 지시하는 것입니다.~
동기화는 atomic한 연산을 보장해줘야 하는 것이고, 이것이 흔히 말하는 임계영역이죠.
이것은 java에서는 sync 내부에서 보장되므로 위의 예제에서 volatile은 굳이 넣지 않아도 됩니당.
Static 변수를 선언할때 null 초기화해야한다는거.
이거 빼놓으면 앱을 껐다가 바로켰을때 의도하지않은 작업이 일어날 수 있습니다