싱글턴 패턴

존스노우·2024년 5월 2일
1

디자인패턴

목록 보기
4/4

어떤용도?

  • 스레드풀 , 캐시 , 사용자 설정 등..
  • 인스턴스가 2개이상이면 사이드이펙트가 일어나는 자원들 와닿지 않는군..

전역 변수의 단점

  • 전역 변수에 객체 대입? 자원이 크다면? 애플리케이션이 끝날때까지 자원이 크다.

고전적인 싱글턴

  • 고전적인 싱글턴 방식으로 초콜레이트 보일러 코드 예시

싱글턴 패턴의 정의

  • 클래스 인스턴스 하나만 만들고

  • 그 인스턴스로 전역 접근 제공

  • 하나뿐인 인스턴스로 관리하게 만듬.

  • 인스턴스가 필요하다면 반드시 클래스 거치도록.

  • 게으른 방식으로 생성?

  • 클래스메소드로 인스턴스를 반환받을수 있다. 언제어디서든 접근가능

멀티스레딩 문제

  • 이런식으로 동시에 2개의 스레드가들어오면
  • 2개의 인스턴스가 만들어지는 문제

문제해결

  • 동기화를 사용
  • 하지만 성능이 100배정도 느려진다
  • 속도가 상관없는 곳은 사용해도 된다 예제 코드처럼.

  • 아니면 처음부터 초기화
  • 이 방법이 가장 무난한거 같다.

  • DCL 더블체킹락킹 방법 인대.
  • 인스턴스가 생성되었는지 확인 및 생성되어 있지않을때만 동기화 방법
  • 처음만 동기화..
  • 근대 괜시리 복잡해지고 딱히..?

volatile

  • 가시성 보장 한 스레드에서 변수 수정시 다른 스레드 항상 수정값 읽음
  • 원자적으로 이루어져 읽기 쓰기가 다른 스레드에 의해 간섭 받지않음.
  • 하지만 변수에대해 원자성만 보장하지 메서드나 코드블록에 원자성 보장하지 않음
public class Counter {
    private volatile int count = 0;

    public void increment() {
        count++;
    }

    public int getCount() {
        return count;
    }
}

이런 코드 예시에서

  • 스레드 A가 count의 값을 읽음. (예: count = 10)
  • 스레드 B가 count의 값을 읽음. (예: count = 10)
  • 스레드 A가 count의 값을 1 증가시키고 저장 (예: count = 11)
  • 스레드 B가 count의 값을 1 증가시키고 저장 (예: count = 11)
  • 이런문제로 DLC 사용하는 듯
public class SharedData {
    private volatile boolean flag = false;

    public void setFlag(boolean value) {
        flag = value;
    }

    public boolean getFlag() {
        return flag;
    }
}

public class ThreadA extends Thread {
    private SharedData data;

    public ThreadA(SharedData data) {
        this.data = data;
    }

    @Override
    public void run() {
        while (!data.getFlag()) {
            // do some work
        }
        System.out.println("ThreadA detected flag change.");
    }
}

public class ThreadB extends Thread {
    private SharedData data;

    public ThreadB(SharedData data) {
        this.data = data;
    }

    @Override
    public void run() {
        data.setFlag(true);
        System.out.println("ThreadB changed flag.");
    }
}

public class VolatileExample {
    public static void main(String[] args) {
        SharedData data = new SharedData();

        ThreadA threadA = new ThreadA(data);
        ThreadB threadB = new ThreadB(data);

        threadA.start();
        threadB.start();
    }
}

  • ThreadA는 flag 변수의 값을 지속적으로 확인하면서, flag가 true가 될 때까지 대기. 일단 flag가 true가 되면, "ThreadA detected flag change."라는 메시지를 출력

싱글톤은 언제사용?

  • 보통 요즘 라이브러리? 좋은게 많이나와서 사용할 일이 별로없을거같다
  • 허나 로컬 캐시만들때 사용하면 괜찮을거같다
public class CacheSingleton {
    private static CacheSingleton instance;
    private Map<String, Object> cache;

    private CacheSingleton() {
        cache = new HashMap<>();
    }

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

    public void put(String key, Object value) {
        cache.put(key, value);
    }

    public Object get(String key) {
        return cache.get(key);
    }
}
profile
어제의 나보다 한걸음 더

0개의 댓글