티스토리 뷰
728x90
1. 의미
- 시스템 런타임이나 환경설정과 같이 오직 하나의 인스턴스만 만들어 제공할때 사용하는 패턴.
2. 작성 방법
- new 키워드를 사용하여 인스턴스를 생성하지 못하도록 막는다.
- static 메서드를 통해 인스턴스를 가져오는 하나의 통로를 만든다.
public class Settings {
private static Settings instance = null;
private Settings() { }
public static Settings getInstance() {
if (instance == null) {
instance = new Settings();
}
return instance;
}
}
public class App {
public static void main(String[] args) {
Settings settings01 = Settings.getInstance();
Settings settings02 = Settings.getInstance();
System.out.println(settings01 == settings02); // true
}
}
3. Thread Safe 구현 방법
- 위의 구현은 멀티 쓰레드 환경에서는 헛점이 발생할 수 있다.
public static Settings getInstance() {
// A쓰레드가 Null체크 후 초기화를 하려고 if문에 들어가려는 상태에서
// B쓰레드가 A쓰레드가 초기화 직전에 Null체크를 하는경우 A, B두 개의 쓰레드가 다른 Settings인스턴스를 가질수 있음.
if (instance == null) {
instance = new Settings();
}
return instance;
}
3.1. synchronized
- synchronized 키워드를 사용하여 getInstance메서드를 하나의 쓰레드만 사용할수 있도록 한다.
- 메서드를 사용할때 마다 동기화 처리를 하기때문에 성능에 이슈가 생길 수 있음.
public static synchronized Settings getInstance() {
if (instance == null) {
instance = new Settings();
}
return instance;
}
3.2. 이른 초기화
- 인스턴스를 초기화하는 비용이 많지 않다면 미리 초기화를 시킨다.
public class Settings {
private static final Settings INSTANCE = new Settings();
private Settings() { }
public static Settings getInstance() {
return INSTANCE;
}
}
3.3. double checked lock
- 위의 방법들 보다 확실하지만 구현하고 이해하기 복잡한 코드가 되버림.
- volatile - Java 변수를 Main Memory에 저장하겠다라는 것을 명시하는 키워드. (https://nesoy.github.io/articles/2018-06/Java-volatile)
- synchronized (Settings.class) - Settings 클래스 기준으로 동기화 시키겠다는 의미
public class Settings {
private static volatile Settings instance = null;
private Settings() { }
public static Settings getInstance() {
if (instance == null) {
synchronized (Settings.class) {
if (instance == null) {
instance = new Settings();
}
}
}
return instance;
}
}
3.4. static inner class 사용 (권장)
- 내부에 static 클래스를 만들어 그안에 멤버 상수로 인스턴스를 가지고있는다.
- 반환시에 static inner 클래스를 통하여 반환하면 반환시점에 클래스가 로딩되어 지연 초기화가 가능하며 멀티쓰레드 환경에서도 안전하다.
public class Settings {
private Settings() { }
private static class SettingsHolder {
private static final Settings INSTANCE = new Settings();
}
public static Settings getInstance() {
return SettingsHolder.INSTANCE;
}
}
4. 싱글톤 패턴을 깨는 방법
- 우회적인 방법으로 인해 싱글톤 패턴을 피해서 새로운 인스턴스를 만들수 있다.
4.1. reflection 사용
public class App {
public static void main(String[] args)
throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
Settings settings = Settings.getInstance();
Constructor<Settings> settingsConstructor = Settings.class.getDeclaredConstructor();
settingsConstructor.setAccessible(true);
Settings settings1 = settingsConstructor.newInstance();
System.out.println(settings == settings1); // false
}
}
4.2. 직렬화 / 역직렬화 사용
- Serializable 한 객체를 직렬화 후에 역직렬화를 하면 새로운 객체를 생성하게 된다.
public class App {
public static void main(String[] args) throws IOException, ClassNotFoundException {
Settings settings = Settings.getInstance();
Settings settings1 = null;
try (ObjectOutput objectOutput = new ObjectOutputStream(new FileOutputStream("setting.obj"))) {
objectOutput.writeObject(settings);
}
try (ObjectInput objectInput = new ObjectInputStream(new FileInputStream("setting.obj"))) {
settings1 = (Settings) objectInput.readObject();
}
System.out.println(settings == settings1); // false
}
}
4.2.1. 역직렬화 대응 방안
- 역직렬화시에 readResolve 메서드를 호출하여 객체를 생성하는데 여기서 기존 객체를 반환시킨다.
public class Settings implements Serializable {
private Settings() { }
private static class SettingsHolder {
private static final Settings INSTANCE = new Settings();
}
public static Settings getInstance() {
return SettingsHolder.INSTANCE;
}
protected Object readResolve() {
return getInstance();
}
}
5. 가장 안전하게 싱글톤을 만드는 방법
- enum을 사용하여 만들면 reflection(enum은 reflection이 막혀있음), 직렬화/역직렬화에 안전하다.
- 다만, 선언과 동시에 초기화 된다는 단점이 존재한다.
public enum Settings {
INSTANCE;
}
위글은 인프런의 코딩으로 학습하는 GoF의 디자인 패턴강의를 정리하였습니다.
백기선님의 수락으로 정리하였으며 더 자세한 내용은 강의를 수강하시기 바랍니다.
https://www.inflearn.com/course/%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4/dashboard
728x90
'디자인 패턴' 카테고리의 다른 글
06. 어댑터 패턴 (0) | 2022.02.10 |
---|---|
05. 프로토타입 패턴 (0) | 2022.02.10 |
04. 빌더패턴 (0) | 2022.02.10 |
03. 추상 팩토리 패턴 (0) | 2022.02.10 |
02. 팩토리 메소드 패턴 (0) | 2022.02.10 |
댓글