이번에 다룰 Singleton 패턴은 저번에 다루었던 Prototype 패턴과 동일한 생성 패턴 중 하나입니다.
하지만, Prototype 패턴보다 훨씬 더 단순하고 쉬운 패턴입니다.
아래의 Singleton 패턴 다이어그램을 보면, 전에 보던 패턴 다이어그램보다 단순하다는 것을 한눈에 파악할 수 있습니다.
하나의 클래스만을 가지고서 Singleton 패턴을 만들 수 있습니다.
💡 Singleton 패턴은 인스턴스가 하나만 존재하는 것을 보증하는 패턴입니다.
여기에서 살펴봐야 할 점은 크게 2가지입니다.
첫 번째로는 밑줄이 그어진 속성입니다.
☝️ 이 때, 밑줄이 그어진 singletion, getInstance는 static를 의미합니다.
두 번째로는 +와 -로 표시된 접근 제어자입니다.
여기에서 +는 public, -는 private를 의미합니다.
singleton과 Singleton 메소드(생성자 메소드)는 private로 되어 있어서 Singleton 클래스 외부에서는 생성자 호출을 금지합니다.
앞서서 singleton 패턴은 인스턴스가 하나만 존재하는 것을 보증한다고 이야기했기 때문에, 하나만 존재하기 위해서 private로 선언해야 합니다!!
위의 Singleton 패턴 다이어그램을 자바 코드로 옮겨보았습니다!
🤔 이때, 아래의 코드에서 이상한 점을 찾으셨나요??
public class Singleton(){
private static Singleton singleton;
private Singleton() {}
public static Singleton getInstance() {
if(singleton == null){
singletone = new Singleton();
}
return singleton;
}
}
자바 언어는 멀티스레드를 사용하는 언어이기 때문에 여러 스레드가 동시에 getInstance() 메소드에 접근하는 경우 동시에 new Single()을 여러 개 생성할 수 있습니다.
즉, 싱글 패턴은 하나만 존재한다는 것을 보증해야 하는데, 위의 코드에서는 보증을 할 수 없습니다!
public class Singleton(){
private static Singleton singleton;
private Singleton() {} //싱글톤 밖에서 new를 할 수 없다.
public static Singleton getInstance() {
if(singleton == null){
//여러 쓰레드가 동시에 Singleton을 접근할 때 동시에 생성될 수도 있다. (단일성을 보장 X)
singletone = new Singleton();
//→ 멀티 쓰레드인 상황이 고려되지 않았다. (자바는 멀티 쓰레드)
}
return singleton;
}
}
그러면 이 코드를 어떠한 방식으로 수정할 수 있을까요??
멀티 스레드 중에서 하나만 접근할 수 있도록 synchronized
를 해당 메소드에 사용할 수 있습니다.
이때의 synchronized는 여러 스레드가 하나의 공유자원에 동시에 접근하지 못하도록 막는 데에 사용됩니다.
public class Singleton(){
private static Singleton singleton;
private Singleton() {}
public static synchronized Singleton getInstance() {
if(singleton == null){
singletone = new Singleton();
}
return singleton;
}
} //synchronized -> lock을 건다.
하지만, 이렇게 읽는 용도의 메소드인 getInstance() 함수에 lock을 거는 것은 자바 언어에서는 효율적이지 않기 때문에 적절하지 않습니다!
☝️ lock을 거는 상황
- 쓰거나 수정을 할 때, Lock을 걸어서 마지막에 누가 수정했는지를 알아야 합니다.
- 그러나, 읽는 상황에서는 쓰거나 수정하는 상황보다 훨씬 빈도수가 많고 누가 마지막에 읽었는지 보장하지 않아도 되기 때문에 lock을 걸지 않습니다!
따라서 아래와 같이 private static Singleton singleton = new Singleton();
을 선언하여 하나만 생성되는 것을 보장할 수 있습니다.
public class Singleton(){
private static Singleton singleton = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return singleton;
}
}