[CS] 싱글톤 패턴

이준기·2022년 11월 1일
0

싱글톤 패턴이란?


어플리케이션이 시작될 때 어떤 클래스가 최초 한번만 메모리를 할당하고(static) 그 메모리에 인스턴스를 만들어 사용하는 디자인 패턴이다.

즉, 메모리 낭비를 방지할 수 있고, 전역이기 때문에 다른 클래스의 인스턴스들이 데이터를 공유하기 쉽다!

구조

public class Singleton {
    // static 으로 선언하여 전역에서 단독으로 존재하는 인스턴스 보장
    private static Singleton instance;

    // private 생성자로 외부에서 객체 생성을 막음
    private Singleton() {
    }

    // 외부에서는 getInstance() 로 instance 를 반환
    public static Singleton getInstance() {
        // instance 가 null 일 때만 생성
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

문제점

  • 하지만, 공유되는 객체기 때문에 멀티 쓰레드 환경에서 Race Condition이 발생할 수 있다. (syncronized 키워드로 락을 걸어서 해결할 수 있지만, 마치 멀티쓰레드가 싱글쓰레드 처럼 동작이 되므로 효울이 매우 안좋아짐..)
  • instance = new Singleton(); 처럼 구체 클래스에 의존하게 되므로, DIP와 OCP 원칙을 위반하게 되어 유연성이 매우 떨어진다.
  • 테스트하기가 불편하다. 싱글톤 인스턴스는 공유되기 때문에, 테스트 환경에서 격리해서 쓰기 위해서는 매번 인스턴스의 상태를 초기화 시켜주어야한다.

스프링 싱글톤


스프링 컨테이너에서는 싱글턴 패턴을 적용하지 않아도 등록한 스프링 빈을 싱글톤으로 관리한다. 이러한 기능 덕분에 싱글톤 패턴의 모든 단점을 해결하고 객체를 싱글톤으로 유지할 수 있다.

싱글톤을 어떻게 보장하지?

  • 스프링에서 defalut로 프록시 객체를 생성해주는 방식인 CGLIB이 해준다.
  • @Configuration이 붙은 클래스에 속한 @Bean들을 등록할 때 프록시 CGLIB 클래스가 내부 로직을 구현하여, 싱글톤 패턴을 적용해서 스프링 빈으로 등록한다.

Stateful 싱글톤 객체

스프링 컨테이너에서 싱글톤으로 관리된다고 하여도, 만약 객체의 상태가 유지된다면(Stateful) thread-safe 하지 못하게된다. 즉, 무상태(Stateless)로 설계되어야 한다.

public class StatefulService {

    private int price; //상태를 유지하는 필드

    public void order(String name, int price) {
        this.price = price; //특정 클라이언트에 의해 상태값이 변경된다.
    }

    public int getPrice() {
        return price;
    }
}

StatefulService statefulService1 = ac.getBean("statefulService", StatefulService.class);
StatefulService statefulService2 = ac.getBean("statefulService", StatefulService.class);

//ThreadA : A사용자 10000원 주문
statefulService1.order("userA",10000);
//ThreadB : B사용자 20000원 주문
statefulService2.order("userB",20000);

//ThreadA : 사용자 A주문 금액 조회
int price = statefulService1.getPrice();
//ThreadA : 사용자 A는 10000원 기대했지만, 기대와 다르게 20000원 출력

→ 공유하는 필드가 있으면 안된다!!! (상태가 유지됨)

→ 특정 클라이언트가 값을 변경할 수 있는 필드가 있으면 안된다.

동시성을 어떻게 해결하지?

→ 스프링에서는 보통 불변 객체를 빈으로 등록하여 방지한다던가, 가변 객체가 존재하더라도 synchronized 키워드나 concurrent 패키지의 클래스들을 사용하여 동시성 문제를 해결했을 것이다. 또한 스프링 빈 사이의 데이터를 주고받을 때에는 스프링 빈의 상태를 변경 시키는 것이 아니라 메소드의 파라미터를 이용했을 것이다. 즉, 스프링이 지향하는 객체지향적 관행을 따라하다 보니 자연스럽게 Thread-safe하게 개발이 된다.

Reference


https://tecoble.techcourse.co.kr/post/2020-11-07-singleton/

https://alwayspr.tistory.com/11

https://velog.io/@syleemk/Spring-Core-싱글톤-컨테이너

https://velog.io/@lshn1007/Spring-11-싱글톤-컨테이너-CGLIB

profile
Hongik CE

0개의 댓글