싱글톤 패턴이란
인스턴스를 오직 한 개만 제공하는 클래스이다.
시스템 런타임, 환경 세팅에 대한 정보 등, 인스턴스가 여러 개일때 문제가 생길 수 있다.
따라서 하나의 인스턴스만 만들어 제공하는 클래스를 만드는 것이 싱글톤 패턴이다.
싱글톤 패턴 구현법 1 - 싱글 스레드
생성자를 private로 만드는 이유 → new로 만들지 않기 위해서
getInstance()를 static으로 만든 이유 → 처음 만들어진 이후엔 null이 아니기 때문에 하나의 인스턴스만 리턴하게 된다.
getInstance()가 멀티 쓰레드 환경에서 안전하지 않은 이유
1번, 2번 스레드가 있다. 1번 스레드가 처음 접근할때 null이기 때문에 new를 실행하게 된다. 그런데 거의 동시에 2번 스레드가 접근할때에도 new를 실행하게 된다. 따라서 두 개의 인스턴스가 생성되게 되는 것이다.
public class Settings {
private static Settings instance;
private Settings() {}
public static Settings getInstance() {
if(instance==null)instance= new Settings();
returninstance;
}
}
public class App {
public static void main(String[] args) {
//Settings settings = new Settings();는 할 수 없다.
Settings settings = Settings.getInstance();
Settings settings1 = Settings.getInstance();
}
}
싱글톤 (Singleton) 패턴 구현 방법 2 - synchronized
해당 메서드의 접근에 동기화를 걸기 때문에 성능에 단점이 생길 수 있다.
메서드 락이다..
public static synchronized Settings getInstance() {
if (instance == null) {
instance = new Settings();
}
return instance;
}
싱글톤 (Singleton) 패턴 구현 방법 3 - eager initialization
이른 초기화 사용법 → 쓰레드 세이프하다.
미리 인스턴스를 만드는 과정 자체가 단점이 될 수 있다.
리소스가 많이 소모되는 인스턴스라면 이른 초기화 방법은 비용이 많이 든다.
private static final Settings INSTANCE = new Settings();
private Settings() {}
public static Settings getInstance() {
return INSTANCE;
}
싱글톤 (Singleton) 패턴 구현 방법 4 - double checked locking
이미 인스턴스가 우연히 없을때만 싱크를 걸기 때문에 효율적인 코드가 될 수 있다.
volatile을 추가해야 한다.. 자바에서 멀티 쓰레드를 다루는 방법
public class SettingsDoubleChecked {
private static volatile SettingsDoubleChecked instance;
private SettingsDoubleChecked() {}
public static SettingsDoubleChecked getInstance() {
if (instance == null) {
synchronized (SettingsDoubleChecked.class) {
if (instance == null) {
instance = new SettingsDoubleChecked();
}
}
}
return instance;
}
}
싱글톤 (Singleton) 패턴 구현 방법 5 - inner
이 방법은 static final를 썼는데도 왜 지연 초기화 (lazy initialization)라고 볼 수 있는가?
필드에서 생성하지 않고 클래스를 만들었기 때문에 getInstance가 호출할때만 초기화되기 때문이다.
public class SettingsInner {
private SettingsInner() {}
private static class SettingsHolder {
private static final SettingsInner INSTANCE = new SettingsInner();
}
public static SettingsInner getInstance() {
return SettingsHolder.INSTANCE;
}
}
싱글톤 (Singleton) 패턴 구현 깨트리는 방법 1 - 리플렉션
리플렉션으로 싱글톤을 해친다.
리플렉션이란?
public class App {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
Settings settings = Settings.getInstance();
Constructor<Settings> constructor = Settings.class.getDeclaredConstructor();
constructor.setAccessible(true);
Settings settings1 = constructor.newInstance();
}
}
싱글톤 (Singleton) 패턴 구현 깨트리는 방법 2 -(역)직렬화
자바 직렬화 역직렬화란
serializabled란
try-resource 블럭이란
public class Settings implements Serializable {
private static Settingsinstance;
private Settings() {}
public static Settings getInstance() {
if(instance==null)instance= new Settings();
returninstance;
}
}
public class App {
public static void main(String[] args) throws IOException, ClassNotFoundException {
Settings settings = Settings.getInstance();
Settings settings1 = null;
try(ObjectOutput out= new ObjectOutputStream(new FileOutputStream("settings.obj"))) {
out.writeObject(settings);
}
try (ObjectInput in = new ObjectInputStream(new FileInputStream("settings.obj"))) {
settings1 = (Settings) in.readObject();
}
System.out.println(settings == settings1);
}
}
싱글톤 (Singleton) 패턴 구현 방법 6 - enum 완벽한 방법,직렬화에 안전
- enum으로 싱글톤 타입을 구현할 때의 단점은?
- 클래스를 쓰는 순간 미리 만들어진다는 단점.
- 상속이 되지 않는다.
- 직렬화 & 역직렬화 시에 별도로 구현해야 하는 메소드가 있는가? enum은 serializable를 별다른 것을 하지 않아도 직렬화 역직렬화가 가능하다.
public enum Settings {
INSTANCE;
}
자바에서 싱글톤이 안티 패턴이 될 수 있는 이유
아래 코드처럼 전역 상태로 인스턴스를 불러올 수 있다는 장점이 있다.
그러나 아래 이유들로 인해 안티 패턴으로 불리기도 한다.
- private 생성자로 인해 상속이 불가능하다. → private는 상속이 불가능
- 테스트하기가 어렵다. → 제한적으로 만들어지기에 mock 객체를 만들기 어렵다.
- static으로 만들어져 있기 때문에 권장되지 않는 방식이다.
public class Settings {
private static Settings instance;
private Settings() {}
public static Settings getInstance() {
if(instance==null)instance= new Settings();
returninstance;
}
}
스프링에서는 위의 단점들을 해결한다.
스프링은 싱글톤 레지스트리를 이용해 싱글톤을 구현한다.
JVM 단계에서 구현하지 않고 스프링이 직접 객체를 만들고 관리한다.
따라서 static과 private로 만들지 않아도 된다.
해당 클래스를 빈으로 만들어 컨테이너에게 넘기게 되면 해당 컨테이너가 제어권을 넘겨받게 되는 것이다. (IOC: 제어의 역전)
참고 자료
'기술면접 > 디자인 패턴' 카테고리의 다른 글
[디자인 패턴] 커맨드 패턴 (0) | 2022.04.05 |
---|---|
[디자인 패턴] 전략 패턴 (0) | 2022.04.05 |
[디자인 패턴] 프록시 패턴이란? (0) | 2022.03.22 |
댓글