애플리케이션이 시작될 때 어떤 클래스가 최초 1번만 메모리를 할당하고 해당 메모리에 인스턴스를 만들어서 사용하는 패턴
즉, 싱클톤 패턴은 하나의 인스턴스만 생성하여 사용하는 디자인 패턴, 인스턴스가 필요하면 똑같은 인스턴스를 만들지 않고 기존에 있는 인스턴스를 활용하는 것
생성자가 여러 번 호출되도 실제로 생성되는 객체는 하나이며 최초로 생성된 이후에 호출된 생성자는 이미 생성한 객체를 반환시키도록 만든다.
→ 생성자를 private으로 선언하여 다른 곳에서 생성하지 못하도록 만들고 getInstance() 메서드를 통해 받아서 사용하도록 구현해준다.
- 객체를 생성할 때마다 메모리 영역을 할당 받아야 한다. 하지만 한번의 new를 통해 객체를 생성해준다면 메모리 낭비를 방지할 수 있고 싱글톤으로 구현한 인스턴스는 전역이므로 다른 클래스의 인스턴스들이 데이터를 공유하는 것이 가능하다.
- 공통된 객체를 여러 개 생성해서 사용해야하는 상황 → 데이터베이스에서 커넥션풀, 스레드풀, 캐시, 로그 기록 객체 등
- 안드로이드 앱에서 각 액티비티 들이나 클래스마다 주요 클래스들을 하나하나 전달하는게 번거롭기에 싱글톤 클래스를 만들어 어디서든 접근하도록 설계
- 다만, 객체 지향 설계 원칙 중 개방 폐쇄 원칙에 위배될 가능성이 크다.
- 싱글톤 인스턴스가 혼자 너무 많은 일을 하거나 많은 데이터를 공유시키면 다른 클래스들 간의 결합도가 높아질 가능성이 크다.
- 결합도가 높아지면 유지보수가 힘들고 테스트도 원활하게 진행할 수 없다.
- 멀티 스레드 환경에서 동기화 처리를 하지 않았을 때 인스턴스가 2개 생성되는 문제도 발생할 수 있으며 반드시 싱글톤이 필요한 상황이 아니면 지양하는 것이 좋다고 한다.
멀티 스레드 환경에서 안전한 싱글톤을 만드는 법
//1 초기화 지연
public class ThreadSafe_Lazy_Initialization {
private static ThreadSafe_Lazy_Initialization instance;
private ThreadSafe_Lazy_Initialization() { }
public static synchronized ThreadSafe_Lazy_Initialization getInstance() {
if (instance == null) {
instance = new ThreadSafe_Lazy_Initializtion();
}
return instance;
}
}
- private static 으로 인스턴스 변수를 만들고
- private 로 생성자를 만들어서 외부에서 생성을 막는다
- 그리고 synchronized 동기화를 활용하여 스레드를 안전하게 만든다 → 다만 synchronized를 사용하면 큰 성능저하를 발생시키므로 지양
//2 초기화 지연+더블 락 체크
public class ThreadSafe_Lazy_Initialization {
private volatile static ThreadSafe_Lazy_Initialization instance;
private ThreadSafe_Lazy_Initialization () { }
public static ThreadSafe_Lazy_Initialization getInstance() {
if (instance == null) {
synchronized (ThreadSafe_Lazy_Initialization.class) {
if (instance == null) {
instance = new ThreadSafe_Lazy_Initialization();
}
}
}
return instance;
}
}
- private static으로 인스턴스 변수를 만들고
- private 생성자를 만들어서 외부에서 생성을 막고
- getInstance() 메서드에서 일차적으로 인스턴스의 존재 여부를 확인하고 synchronized를 통하여 동기화를 시켜 인스턴스를 생성하는 방법
스레드를 안전하게 만들면서 처음 생성 이후 synchronized를 실행하지 않기에 성능 저하 완화가 가능
//3 holder에 의한 초기화 -> 가장 많이 사용되는 일반적 싱글톤 클래스 사용법
public class Something {
private Something() { }
private static class LazyHolder {
public static final Something INSTANCE = new Something();
}
public static Something getInstance () {
return LazyHolder.INSTANCE;
}
}
동기화를 사용하지 않는 이유는 개발자가 직접 동기화에 대한 코드를 작성하면서 회피하려고 하면 프로그램 구조가 그만큼 복잡해지고 비용 문제가 발생할 수 있기에 JVM의 클래스 초기화 과정에서 보장되는 원자적 특성을 사용하여 싱글톤의 초기화 문제에 대한 책임을 JVM에게 떠넘기는 걸 활용한다.
클래스 안에 선언한 클래스인 LazyHolder에서 선언된 인스턴스는 static이므로 클래스 로딩 시점에서 한번만 호출되고 final을 통해 다시 값이 할당되지 않도록 만드는 방식을 사용
'기초 CS 정리' 카테고리의 다른 글
PostCSS 란 (0) | 2023.08.05 |
---|---|
오픈API 사용하여 웹 사이트 만들기 (0) | 2023.05.04 |
디자인 패턴 - 어댑터 패턴 (0) | 2023.03.05 |
JWT (Json Web Token) (0) | 2023.02.26 |
OAuth (Open Authorization) (0) | 2023.02.25 |