싱글톤 사용은 유용하다고 느꼈다.
앱을 만들때 공통적으로 사용하는 api 라든지 , 메소드 등 반복적인 사용이 필요한 부분들이 있는데 이걸 계속 생성자로 생성해주거나 단순히 메모리에 올려 전역으로 사용한다면 성능상 문제가 생길것이다.
이러한 부분을 방지하기 위해 싱글톤이 있었고 싱글톤은 메모리에 올림으로 인한 누수 나 지속적인 생성사용을 방지한다.
이때, lazy singleton 방식이 있는데 이는 기존 싱글톤 방식에서 더욱 효율적인 방법으로 메모리를 다루는다고 보면 된다.
밑의 코드는 기본적인 싱글톤 코드이다.
public class Singleton {
private static Singleton instance;
private Singleton() {
}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
synchronized 로 동기화를 적용하고 있기에 스레드 관련해서 안정성을 부여했다.
그리고 밑에 코드는 아까 언급한 lazy_singleton 방식이라 생각하면 된다.
더블체크를 하는 기능을 추가한것이다.
public class Singleton {
private static volatile Singleton instance;
private Singleton() {
}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton(); }
}
}
return instance; }
}
혹은 내가 사용하고 있는 Inner class 방식이다.
public class Singleton {
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton() {
}
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
자 이렇게 위처럼 싱글톤에 대해 대략적인 내용을 살펴보았다. 위의 설명은 싱글톤에 대해 어떤 느낌인지 주려고 만든 예제 내용이다. 싱글톤에 대해 다시한번 정리해보도록 하자 !
싱글톤(Singleton) 패턴이란?
객체의 인스턴스를 단 하나만 생성하고, 어디서든 동일한 인스턴스를 사용하도록 보장하는 디자인 패턴
1. 왜 싱글톤을 사용할까?
- 전역적으로 하나의 객체를 공유해야 할 때 사용
- 객체가 여러 개 생성되는 것을 방지하여 메모리를 절약
- 데이터를 일관되게 유지 (예: 설정 정보, 데이터베이스 연결, 로그 관리 등)
2. 싱글톤 구현 방법 (Java)
① 기본적인 싱글톤 구현 (Lazy Initialization)
public class Singleton {
private static Singleton instance; // 유일한 인스턴스 저장
private Singleton() {} // private 생성자로 외부에서 객체 생성 방지
public static Singleton getInstance() {
if (instance == null) { // 최초 호출 시 객체 생성
instance = new Singleton();
}
return instance;
}
}
💡 특징:
- 필요할 때만 객체를 생성 (Lazy Initialization)
- 단일 인스턴스 유지
- 멀티스레드 환경에서는 동시 접근 문제가 발생할 수 있음
② Thread-safe 싱글톤 (synchronized)
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
💡 synchronized 키워드를 사용하여 멀티스레드 환경에서도 안전하게 만듦.
⚠️ 하지만, synchronized는 성능 저하를 유발할 수 있음.
③ 이른 초기화 (Eager Initialization)
public class Singleton {
private static final Singleton instance = new Singleton(); // 미리 생성
private Singleton() {}
public static Singleton getInstance() {
return instance;
}
}
💡 특징:
- 클래스가 로드될 때 인스턴스를 생성 (항상 메모리를 차지함)
- 멀티스레드에서 안전하지만, 객체를 사용하지 않더라도 미리 생성됨
④ DCL(Double-Checked Locking) 방식 (권장)
public class Singleton {
private static volatile Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) { // 첫 번째 체크
synchronized (Singleton.class) {
if (instance == null) { // 두 번째 체크
instance = new Singleton();
}
}
}
return instance;
}
}
💡 특징:
- volatile을 사용하여 CPU 캐시 문제 방지
- synchronized 블록을 최소화하여 성능 개선
- 가장 효율적인 싱글톤 구현 방식 중 하나
⑤ 정적 내부 클래스 (권장)
public class Singleton {
private Singleton() {}
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
💡 특징:
- 클래스가 로드될 때 SingletonHolder 내부 클래스는 로드되지 않음
- getInstance()가 호출될 때만 INSTANCE가 생성됨 (Lazy Initialization)
- 스레드 안전하며, 성능도 좋음 (추천 방식)
3. 싱글톤 사용 예시
① 로그 관리
public class Logger {
private static Logger instance;
private Logger() {}
public static Logger getInstance() {
if (instance == null) {
instance = new Logger();
}
return instance;
}
public void log(String message) {
System.out.println("[LOG]: " + message);
}
}
// 사용
Logger.getInstance().log("앱이 시작됨!");
② 데이터베이스 연결 관리
public class DatabaseConnection {
private static DatabaseConnection instance;
private DatabaseConnection() {}
public static DatabaseConnection getInstance() {
if (instance == null) {
instance = new DatabaseConnection();
}
return instance;
}
public void connect() {
System.out.println("데이터베이스 연결됨!");
}
}
// 사용
DatabaseConnection.getInstance().connect();
4. 싱글톤의 장점 & 단점
✅ 장점
- 메모리 절약 (객체를 하나만 생성)
- 데이터 일관성 유지 (모든 곳에서 동일한 객체 사용)
- 전역 접근 가능 (어디서든 쉽게 호출)
❌ 단점
- 멀티스레드 환경에서 주의 필요 (잘못 구현하면 동시 접근 문제 발생)
- 전역 상태 공유로 인해 코드가 복잡해질 수 있음
- 테스트하기 어려움 (Mocking이 어려움)
5. 싱글톤을 언제 사용할까?
✅ 사용할 때
- 로그 관리
- 데이터베이스 연결
- 설정 값 저장 (예: 앱 환경 설정)
- 네트워크 요청 관리
❌ 사용하지 말아야 할 때
- 많은 상태를 관리하는 경우 (전역 상태 오염 가능)
- 단일 책임 원칙(SRP)에 위배될 가능성이 있을 때
- 멀티스레드에서 동시성 이슈가 중요한 경우
■ 결론
- 싱글톤은 객체를 하나만 유지해야 하는 경우 유용
- 멀티스레드 환경에서 올바르게 구현해야 안전함
- 너무 남용하면 코드 복잡도 증가 & 테스트 어려움
- DCL 방식 또는 정적 내부 클래스 방식이 가장 추천됨! 🚀
이제부터 제대로 싱글톤에 대해 사용해보도록 하자 !!!
'Java' 카테고리의 다른 글
Java_ 지역변수 , 인스턴스 변수 차이 (0) | 2024.09.12 |
---|---|
Java_ runOnUiThread , view.post , handler 의 차이 (0) | 2024.09.12 |
Java_ Rxjava blockingGet , subscribe ... (0) | 2024.08.25 |
AOS - Rxjava3 , Single , Disposable (0) | 2024.06.23 |
AOS_ HashMap 과 TreeMap 의 차이 (0) | 2024.03.04 |