생성자를 private으로 선언하여 클래스 밖에서 인스턴스 생성을 막는다.
public class Settings {
private static Settings instance;
private Settings() {}
public static Settings getInstance() {
if(instance == null) {
instance = new Settings();
}
return instance;
}
}
만약 1번스레드가 if문을 타고 인스턴스 생성을 완료하지 못했는데 2번 스레드도 if문을 탄다면
아직 인스턴스가 생성되지 않았기 때문에 2번 스레드도 new Settings()로 객체를 생성하게 됨
(가장 간편한 방법이지만 성능상에 불이득이 생길 수 있음)
public class Settings {
private static Settings instance;
private Settings() {}
public static synchronized Settings getInstance() {
if(instance == null) {
instance = new Settings();
}
return instance;
}
}
(메소드에 여러 쓰레드가 들어와서 작업하더라도
이미 생성된 instance를 반환하기 때문에 쓰레드 safe하다)
public class Settings {
private static final Settings INSTANCE = new Settings();
private Settings() {}
public static Settings getInstance() {
return INSTANCE;
}
}
(매우 많은 트래픽 발생시 if문안에 여러 쓰레드가 동시에 들어올 수 있는데 그럴 경우에만 synchronized하게 효율적으로 처리, 이른 초기화와 달리 객체 사용시점에 인스턴스 생성 )
public class Settings {
private static volatile Settings instance;
private Settings() {}
public static Settings getInstance() {
if(instance == null) {
synchronized (Settings.class) {
if(instance == null) {
instance = new Settings();
}
}
}
return instance;
}
}
(getInstance() 가 호출될때 SettingHolder가 로딩되기 때문에 지연로딩, 스레딩 safe함)
#권장되는 방법중 하나
public class Settings {
private Settings() {}
private static class SettingHolder {
private static final Settings INSTANCE = new Settings();
}
public static Settings getInstance() {
return SettingHolder.INSTANCE;
}
private Object readResolve() {
return getInstance();
}
}
하지만 리플렉션이나 직렬화(객체를 파일로저장), 역직렬화(파일을 객체로변환)시 싱글톤이 깨질 수 있음
직렬화, 역직렬화시엔 readResolve() 라는 메소드가 쓰이는데 이때 new Settings()를하여 싱글톤이 깨진다
(enum 클래스는 리플렉션 사용 시 예외를 던지고 Serializable가 구현되어있으며
직렬화, 역직렬화 시에도 같은 인스턴스 보장함)
public enum Settings {
INSTANCE;
}
만약 싱글톤 패턴을 구현하는데 있어서 상속을 받아야 하거나 즉시로딩이 아닌 지연로딩이 적합하다 판단된다면 holder방식의 static inner class방식을 사용하는 것이 적합하다.
싱글톤의 개념만 이해했지만 다른 여러방식을 통해 멀티스레드 환경 및 싱글톤이 깨지는 경우에 어떻게 대처해야 하는지 알 수 있었고, static inner class의 실행시점이 신기했다
이 부분은 jvm을 공부해서 포스팅 해야겠다.
출처 : 인프런 백기선 - 디자인 패턴