티스토리 뷰

728x90
반응형

목차

     

    Singleton Pattern

     

    싱글톤 패턴은 자바에서 이용되는 디자인 패턴 중 하나다(스프링 부트가 아니다!).

     

    주로 아래와 같은 상황을 해결하기 위해 사용되며,

     

    • 공유자원 관리
      디비 연결, 로깅, 캐싱과 같은 자원은 시스템 전체에서 한 번만 생성되면 충분하다.
    • 비용이 많이 드는 객체 생성
      같은 객체가 빈번하게 생성되는 경우 비용이 많이 든다. 이 경우 싱글톤 패턴을 이용해 하나의 인스턴스를
      생성한 뒤에 해당 인스턴스를 재사용함으로써 리소스 낭비를 줄일 수 있다.
    • 전역 변수 사용 제한
      전역 변수는 시스템 복잡도를 증가시키고 지역 변수와 충돌할 가능성도 있다.
      이를 해결하기 위해 싱글톤 패턴을 이용해 전역변수의 사용을 최소화할 수 있다.

    보통 아래와 같은 방식으로 구현하게 된다.

     

    • 생성자를 private으로 선언해 외부의 접근을 막는다.
    • 클래스 내부에서 자신의 인스턴스를 private static으로 유일하게 선언한다.
    • 유일한 인스턴스를 반환하는 getInstance() 메서드를 public static으로 선언하여 외부 접근을 허용한다.
      해당 메서드는 클래스의 인스턴스를 반환하며 인스턴스가 없는 경우 생성해 준다.
    • 멀티스레드 환경의 문제를 막기 위해 synchronized 키워드를 사용한다.
    • 직렬화가 필요하다면 Serializable 인터페이스를 구현한다.

    이렇게 구현된 싱글톤 패턴 클래스는 대략 아래와 같은 모습을 가진다.

    public class Singleton {
    
    	private static Singleton instance;
    
    	private Singleton() {
    		// 외부 객체 생성 방지를 위해 private 설정
    	}
    
    	public static synchronized Singleton getInstance() { // 멀티스레딩을 위한 synchronized 키워드
    		if(instance == null) {
    			instance = new Singleton(); // 인스턴스가 없으면 만들어준다.
    		}
    		return instance;
    	}
    }

    하지만 여기서 synchronized 키워드가 문제가 되는데,

     

    해당 키워드는 메서드에 대한 동기화를 보장하기 위한 처리 때문에 약 10배에서 100배 정도 성능 저하가 발생할 수 있다.

     

    물론 최근의 JVM은 계속해서 해당 키워드를 최적화하고 개선하려는 노력을 하고 있기는 하지만,

     

    그대로 느리다.

     

    따라서 synchronized 키워드의 사용을 최대한 줄이면서도 멀티스레드 환경을 대비할 필요성이 생긴다.

     

    Double-Checked Locking

     

    이때 필요한 것이 바로 <Double-Checked Locking> 방식이다.

     

    이 방식은 이름대로 인스턴스의 생성 여부를 더블체크하는 방식이며, 인스턴스가 생성되지 않았을 경우에만

     

    synchronized 블록에 들어가 객체를 생성하고, 그렇지 않을 경우 바로 객체를 반환한다.

     

    코드로 보면 아래와 같다.

    public class Singleton {
    
    	private volatile static Singleton instance;
    
    	private Singleton() {}
    
    	public static Singleton getInstance() {
    		Singleton result = instance;
    		if (result == null) { // result가 비어있는지 체크
    			synchronized (Singleton.class) {
    				if (instance == null) { // 인스턴스 생성여부 체크
    					instance = new Singleton(); // 없다면 생성
    				}
    				result = instance; // 있다면 바로 반환
    			}
    		}
    		return result;
    	}
    }

    여기서 보면 인스턴스 앞에 volatile이라는 키워드가 붙은 것을 확인할 수 있다.

     

    이 키워드는 직역하면 '휘발성의'라는 뜻이며, 해당 키워드가 붙은 변수의 최적화를 막는다.

     

    최적화를 막는다고 하니 이상하게 들릴 수도 있겠지만, 여기선 인스턴스에 대한 캐싱을 막아 언제나 최신 값을 제공하도록

     

    보장하는 역할을 한다. 만약 위 키워드를 사용하지 않으면 대기 중인 작업이 잘못된 값을 반영하거나

     

    무한루프에 빠질 가능성이 있다고 한다.

     

    Initialization-On-Demand Holder Idiom

     

    이어서 또 다른 방법으로는 <Initialization-On-Demand Holder Idiom>이라는 패턴이 존재하는데,

     

    이는 싱글톤 인스턴스를 클래스 로딩 시점에 미리 생성하지 않고 필요할 때 생성하는(On-Demand) 패턴이다.

     

    우선 코드를 보자.

    public class Singleton {
    
    	private Singleton() {
    	}
    
    	private static class LazyHolder {
    		public static final Singleton INSTANCE = new Singleton();
    	}
    
    	public static Singleton getInstance() {
    		return LazyHolder.INSTANCE;
    	}
    }

    먼저 LazyHolder라는 내부 클래스를 정의해 싱글톤을 초기화시켰다.

     

    이 방식은 위에서 설명했듯 클래스 로딩 시점의 인스턴스 생성을 막는 역할을 한다.

     

    그 이유는 내부 클래스 자체의 특성 때문인데, 내부클래스는 클래스 로딩 시점이 아닌 클래스가 최초로 사용될 때 초기화 된다.

     

    때문에 서로 다른 스레드에서 동시에 인스턴스를 호출해도 LazyHolder의 초기화는 한 번만 이루어지며,

     

    이는 다중 스레드에서 싱글톤 패턴을 안전하게, 그러면서도 불필요한 동기화 없이 수행할 수 있도록 한다.

     

    Pros and Cons

     

    앞서 싱글톤 패턴은 스프링부트의 패턴이 아니라는 것을 언급했다.

     

    그 이유는 스프링 프레임워크에서는 객체의 라이프사이클을 의존성 주입을 통해 관리하기 때문이다.

     

    이때 모든 빈(Bean) 객체를 싱글톤으로 생성해 주입하기 때문에 싱글톤 패턴을 대체하기에 충분하며,

     

    따라서 개발자는 싱글톤 패턴보다 의존성 주입 패턴을 더 많이 사용하게 된다.

     

    마지막으로 싱글톤 패턴의 장단점에 대해 정리하고 글을 마치자.

     

    • 장점

      • 단 하나의 인스턴스가 보장된다. 이는 다중 스레드 환경뿐 아니라 리소스 관리에도 이점이 있다.
      • 생성된 인스턴스는 프로그램 전역에서 접근 및 사용이 가능하다
    • 단점

      • 객체지향의 원칙 중 하나인 단일 책임 원칙을 위반할 수 있다.
        하나의 인스턴스가 여러 개의 문제를 해결할 가능성이 있기 때문이다.
      • 생성된 인스턴스에 문제가 생기면 전체 시스템이 영향을 받을 수 있다.
      • 테스트가 어렵다. 싱글톤 객체가 다른 객체에 대한 의존성을 가진 경우 유닛 테스트가 어려워진다.

    따라서 앞서 말한 전역 변수 문제라거나 공유자원 관리 등 반드시 필요한 상황에만 싱글톤 패턴을 사용하고

     

    특별한 경우가 아니라면 의존성 주입과 같은 패턴을 고려하는 것이 필요하다.

    반응형
    댓글
    공지사항
    최근에 올라온 글
    최근에 달린 댓글
    Total
    Today
    Yesterday
    링크
    «   2024/11   »
    1 2
    3 4 5 6 7 8 9
    10 11 12 13 14 15 16
    17 18 19 20 21 22 23
    24 25 26 27 28 29 30
    글 보관함