디자인패턴 - 생성패턴 - 싱글톤(Singleton)

waonderboy·2022년 3월 3일
1

디자인 패턴

목록 보기
2/3
post-thumbnail

싱글톤 패턴

싱글톤 패턴의 정의와 사용

📖 singleton[ˈsɪŋɡltən]
1.(단독) 개체
2.독신자(결혼을 안 했거나 애인이 없는 사람)
3.(쌍둥이가 아닌) 외둥이[단일아]
출처 - 옥스퍼드 영한사전


싱글톤 패턴은 객체의 인스턴스가 오직 1개만 생성되는 패턴을 의미한다. 최초 호출시 인스턴스를 메모리에 고정적으로 할당하고, 그 이후에 인스턴스를 호출할때는 최초에 생성된 인스턴스를 리턴한다.

싱글톤 패턴은 다음과 같은 상황에서 사용한다.

  • DBCP(DataBase Connection Pool)처럼 공통된 객체를 여러개 생성해서 사용해야하는 상황 (쓰레드풀, 캐시, 대화상자, 사용자 설정, 레지스트리 설정, 로그 기록 객체 등)
  • 오직 하나의 객체만 필요한 경우

그러면 싱글톤 패턴을 쓰는 이유는 무엇일까?

  • 고정된 메모리 영역에 최초 한번만 인스턴스를 생성하기 때문에 때문에 메모리 낭비를 방지할 수 있음

  • 싱글톤으로 만들어진 클래스의 인스턴스는 전역 인스턴스이기 때문에 다른 클래스의 인스턴스들이 데이터를 공유하기 쉽다.



싱글톤 패턴 구현

싱글톤 패턴 구현(naive)

디자인 패턴은 특정한 코드나 규약을 의미하는것이 아닌 템플릿 형태로 일반화된 솔루션을 말하기 때문에 분명한 코드가 있는게 아니라 정의와 개념에만 맞게 구현하면 된다.

싱글턴 패턴이 필요한 상황(설정 객체)를 가정하여 구현해보자.

싱글턴 패턴은 외부에서 new 생성자를 이용해 구현하면 안되고, 메모리에 고정적으로 할당해야한다.

이를 고려하면 다음 키워드를 사용하여 구현할 수 있다.

요구조건방법
외부 new 생성자를 이용해 구현하면 안됨getInstance 메소드를 통해 내부에서 인스턴스 생성
메모리에 고정적으로 할당static 사용
public class Settings {
    private static Settings instance;
    private Settings() {}

    public static Settings getInstance() {
        if (instance == null) {
            instance = new Settings();
        }
        return instance;
    }
}

코드 1 - 싱글톤 패턴 적용

public static void main(String args[]) {
        Settings instance1 = Settings.getInstance();
        Settings instance2 = Settings.getInstance();
        System.out.println(instance1 == instance2);
		//True
        Settings2 instance3 = Settings2.getInstance();
        Settings2 instance4 = Settings2.getInstance();
 		System.out.println(instance3 == instance4);
		//True
 }

코드 2 - 메인함수


하지만 위 코드는 문제점을 가지고 있다.


싱글톤 패턴 구현(advanced)

코드 1의 싱글톤 패턴은 멀티스레드시 문제점이 발생한다. if문 도중에 컨택스트 스위칭이 발생하면 인스턴스가 중복생성이 될 수 있다. 순서없이 컨택스트 스위칭이 일어나는 영역(Critical Section, 임계구역)을 lock하여 동기화 할 필요가 있다.

그러면 멀티스레드를 해결 할 수 있는 코드를 만들어 보자.

1. Eager initialization

public class Setting2 {
    private static Setting2 instance = new Setting2();

    private Setting2() {};

    public static Setting2 getInstance() {
        return instance;
    }
}

코드 3 - Eager initialization 방식의 싱글턴

멀티스레드 문제는 해결할 수 있지만 인스턴스를 요청하지 않아도 생성되기 때문에 효율적이지 않다.


2. synchronized

public class Settings3 {
    private static Settings3 instance;

    private Settings3() {}

    public static synchronized Settings3 getInstance() {
        if (instance == null) {
            instance = new Settings3();
        }
        return instance;
    }
}

코드 4 - synchronized 방식의 싱글턴

synchronized 방식을 쓰는 것 자체가 성능상 비효율을 초래한다.


3. static inner class

package singleton;

public class Settings4 {
    private Settings4() {}

    private static class SettingHolder {
        private static final Settings4 INSTANCE = new Settings4();
    }

    public static Settings4 getInstance() {
        return SettingHolder.INSTANCE;
    }
}

코드 5 - static inner class 방식의 싱글턴




싱글턴 패턴의 단점

  • 싱글톤 인스턴스가 많은 데이터를 공유할 경우 다른 클래스의 인스턴스들 간에 결합도가 높아져 "개방-폐쇄 원칙" 을 위배하게 된다.

  • 멀티쓰레드환경에서 위와 같은 동기화처리를 하지 않으면 인스턴스가 두 개나 생성될 수 있다.

  • lifecycle을 제어하기 힘들다. 싱글톤이 제대로 garbage collecting이 되지 않으면 지속적으로 메모리를 점유하고 있을 가능성이 크다.






Reference

profile
wander to wonder 2021.7 ~

0개의 댓글