Search
▫️

싱글톤 패턴_1

싱글톤(Singleton)패턴

인스턴스를 오직 한개만 제공하는 클래스

싱글톤 패턴을 가장 단순히 구현하는 방법

시스템 런타임, 환경 세팅에 대한 정보 등, 인스턴스가 여러개 일 때 문제가 생길 수 있는 경우가 있다.
인스턴스를 오직 한개만 만들어 제공하는 클래스가 필요하다.
public class App { public static void main(String[] args){ //Setting이라는 클래스의 타입의 인스턴스를 여러개 생성할수있다. //new를 이용해서 만들면 새로운 인스턴스가 만들어진다. Setting settings = new Settings(); Setting settings1 = new Settings(); //new로 생성한것들은 서로 같지않다. System.out.println(settings != settings1); //true } }
Java
복사
public class Settings { }
Java
복사
싱글톤 패턴을 사용할려면 절대 new를 사용하면 안된다.
new 생성을 하지 못하게
public class Settings { //이 해당하는 클래스 안에서만 접근할 수 있게 하는 생성자를 만들어준다. private Settings() {} //글로벌엑세스가 가능하게 할려면 public static Settings getInstance() { return new Settings(); } }
Java
복사
public class App { public static void main(String[] args){ Setting settings = Settings.getInstance(); Setting settings1 = Settings.getInstance(); //결국 이것도 Settings 클래스의 static 안에서도 new로 진행했기 때문에 // 다른 객체라고 인식한다.(생성한것들은 서로 같지않다.) System.out.println(settings != settings1); //true } }
Java
복사
우린 현재 인스턴스가 같기를 바라고 있는데 자꾸 다른객체로 인식하고 있다.
public class Settings { private static Settings instance; private Settings() {} public static Settings getInstance() { if(instance == null){ instance = new Settings(); } return instance; } }
Java
복사
public class App { public static void main(String[] args){ Setting settings = Settings.getInstance(); //매번 호출해도 같은 인스턴스로 인식한다. System.out.println(settings == Settings.getInstance()); //true } }
Java
복사
이 방법이 가장 괜찮은 방법이지만 이 방법에 심각한 문제가 하나 있다.
앱 애플리케이션을 만들다보면 멀티쓰레드로 만들어진다.
멀티쓰레드 환경에서 이 코드가 안전하지 않다.

멀티쓰레드 환경에서 안전하게 싱글톤을 생성하는 방법

sychronized 키워드 사용하기

public class Settings { private static Settings instance; private Settings() {} //sychronized 사용해서 이 메소드 안에서 하나의 쓰레드만 들어오게 한다. // 하나의 인스터스만 보장 // 단점은 getInstance()를 호출할때마다 성능의 불이득이 있을 수 있다. // 동기화 자체가 key가 되는 lock을 잡아서 lock을 가지고 있는 쓰레드만 영역에 접근할수있게 하기때문에 // 다 쓰면 lock을 해제하기때문에 성능 부화가 생긴다. public static sychronized Settings getInstance() { if(instance == null){ instance = new Settings(); } return instance; } }
Java
복사

이른 초기화(eager initialization) 사용하기

근데 만약에 getInstance() 메소드 안의 instance = new Settings(); 객체를 꼭 나중에 만들지 않아도 된다 하면은 미리 만들 수도 있다.
public class Settings { private static final Settings INSTANCE = new Settings(); private Settings() {} public static Settings getInstance() { return INSTANCE; } }
Java
복사
이 방법은 바로 INSTANCE를 리턴해주기 때문에 Settings클래스가 로딩되는 시점에 static의 필드들이 초기화 되면서 멀티쓰레드 환경에서도 안전한 방법이 된다.
다만 단점은 미리 만든다는게 단점이 될 수 있다.
만약 이 인스턴스를 만드는 과정이 길고 오래걸리고 메모리를 많이 사용한다면 만들어 놨는데 사용하지 않는다면
(애플리케이션 로딩할때 오래걸리는데 막상 사용하지않는다?…그건..)
여기서 이 인스턴스를 사용이 될 때 만들고 싶다! 근데 sychronized 비용이 너무 신경쓰이면

double checked locking 사용하기

효울적인 동기화 블럭 만들기
public class Settings { //volatile 키워드를 사용해야지만 자바1.5 이상부터 동작하는 double checked locking 사용 가능 private static volatile Settings instance; private Settings() {} public static Settings getInstance() { if(instance == null){ //Settings의 class를 lock으로 이용해주고 sychronized(Settings.class){ if(instance == null){ instance = new Settings(); } } } return instance; } }
Java
복사
instance을 필요로 하는 시점에 만들 수 있다는 큰 장점이 있다.
문제는 사실 이 기법이 복잡한 기법이다.
volatile 을 사용하는 방법까지도 이해하면 좋지만 너무 깊게 빠지게 되고
자바 1.4를 사용하는 사람도 있을 수 있다(사실 거의 희박하지만)
그래서 다른방법 중에 권장하는 방법 중 하나인

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; } }
Java
복사
멀티쓰레드 환경에서도 안전하고 getInstance()가 호출될 때 Settings클래스가 로딩이 되고 SettingsHolder가 만들어지면서 lazy loading도 가능한 코드가 된다
(lazy loading이란 사용자가 필요로 하는 시점에 로딩하는 방법)
이 모든 방법을 깨트릴수있는 다양한 방법이 존재하는데…

*참고(출처)