Rlog

싱글톤(Singleton) Pattern 본문

DesignPattern

싱글톤(Singleton) Pattern

dev_roach 2021. 10. 28. 21:52
728x90

이번 시간에는 그간 묵혀놨던 디자인 패턴을 좀 공부해보려고 한다.

요즘 코드를 치면서 느끼는게 이런 디자인 패턴을 잘 몰라서 뭔가 설계를 할때 어려움을 느끼는게 아닌가? 라는 생각도 든다.

뭐 실력 문제 겠지만, 이런 것을 더 배워나가다 보면 앞으로 조금 더 수월하게 설계를 할 수 있을 거란 생각에 공부해 보려고 한다.

 

싱글톤 패턴

다들 많이 사용해 봤을 것 이고, 유명한 패턴이다.

개념은 인스턴스를 오직 한개만 제공하기 위해 이용하는 패턴이다. 

 

이 패턴이 필요한 이유?

예를 들면 우리가 설정을 하는 메뉴를 여러 인스턴스로 만들게 된다면 인스턴스 마다 설정이 달라지는 등 많은 문제를 발생시킬 수 있다.

어플리케이션에서 오직 한개로 유지해야할 객체를 싱글톤 패턴을 이용해 제공하거나 접근하도록 해야한다.

 

위와 같은 상황이 발생할 수 있다.

우리가 첫번째로 해야할 일은 무엇일까? 바로 밖에서 new 생성자를 사용하지 못하도록 해야한다.

 

이제 위와 같이 생성자에 private 을 걸게 되면 밖에서는 인스턴스 생성이 불가능하게 된다.

 

이제 외부에서 같은 인스턴스를 사용하도록 만들어주자. 

어떻게 만들어줄까? 아주 간단하게는 아래와 같은 방식으로 구현 가능하다.

외부에서 이를 테스트 한번 해보도록 하자.

위와 같이 하니 결과값이 True 가 나오게 되었다.

이제 몇번을 호출하더라도 같은 결과 값이 나오게 된다. 일단은 성공한 것으로 보인다.

 

근데 심각한 문제가 하나 있다. 과연 어떤 문제일까?

멀티스레드 환경을 생각해보자. 이 방법이 안전할까? 만약 안전하지 않다면 왜 안전하지 않을까?

 

Thread 1 번이 인스턴스를 만들고 있을때 Thread 2 번이 저 조건문에 들어가게 되면 어떻게 될까요?

아마도 Thread2 번은 또 하나의 인스턴스를 생성하게 될 것 입니다.

 

그래서 우리가 지금 까지 구현한 방식은 Thread-Safe 하지 않습니다.

 

우리가 보통 자바에서 멀티스레드 환경에서 안전하게 하기 위해서는 synchronized 를 사용할 수 있습니다.

synchronized

하지만 synchronized 를 메소드 자체에 걸게 되면 성능 문제를 야기할 수 있습니다.

 

그럼 간단히 Synchronized 를 쓰지 않기 위해서 Eager initialization 을 사용할 수도 있다. 

아래와 같은 방식으로 말이다.

Eager initialization

이렇게 이용할 시에는 성능문제를 야기할 수 있는 synchronized 를 제거할 수 있다는 장점이 있다.

다만 이 인스턴스가 필요없는 시점에서 미리 만들어야 된다는 단점이 있어. 그렇게 좋은 방식이 아닐 수도 있다.

만약 초반부터 유지되야 한다면 Eager initialization 이 좋은 선택일 수도 있다.

 

그렇다면 또 다른 방법은 없을까?

다른 방법 중 하나로 Double Checked Locking 을 이용한 방법이 있다.

예를 들어 if 문을 어찌어찌 뚫어 내더라도 결국에는 Setting.class 를 누군가 점유하고 있을 것이기 때문에

그 밑의 로직으로 들어갈 수 없게 된다.

 

근데 그래서 처음거랑 무슨차이냐? 라는 말이 있을텐데

요건 이제 인스턴스가 생성되고 나서는 synchronized 에 접근하지 않을 것이므로 처음 것보다는 훨씬 병목현상이 적을 수 밖에 없다.

또한 인스턴스를 필요한 시점에 만들 수 있다는 장점이 있다.

 

하지만 위와 같은 방식의 문제는 무엇일까?

바로 코드의 복잡성 그리고 volatile 키워드를 써야 한다는 것이다.

자바 1.5 이상부터 동작하는 코드이다. 혹시라도 모르는 사람을 위해 간단히 얘기를 하면

스레드는 자신의 지역에 변수를 캐싱하게 되는데 만약 그 정보가 다른 스레드와 공유되어야 하는 무엇이라고 해보자.

스레드간은 값을 공유 하기 위해서 무언가 수단이 필요한데, 그 수단을 volatile 이라는 키워드가 도와준다.

대략 설명한 것 이니 더 공부하고 싶은 사람은 심층있게 공부해 보길 바란다.

 

뭐 여튼 저런 위의 복잡성으로 인해 내부 static class 를 이용하는 방법이 있다.

 

이렇게 했을때의 장점은 Settings 를 호출할때 Lazy 하게 객체가 생성이 가능하다.

또한 Multi-Thread 환경에서도 Thread-Safe 함을 알 수 있다.

'DesignPattern' 카테고리의 다른 글

의존성 역전 원칙(Dependency Inversion principle)  (1) 2023.08.13
팩토리 메소드 패턴  (0) 2021.10.30