Skip to content

Latest commit

 

History

History
197 lines (121 loc) · 6.46 KB

Singleton.md

File metadata and controls

197 lines (121 loc) · 6.46 KB

Singleton 패턴

Singleton 패턴이란?

Singleton패턴은 클래스에 객체가 단 하나만 필요한 경우에 사용되는 디자인 패턴이다. 예를 들어 시스템 안에서 1개밖에 존재하지 않는 것(ex. 컴퓨터 자체, 윈도우 시스템)을 프로그램으로 표현하고 싶을 때 사용한다.

프로그래머가 주의를 기울여 객체를 1개만 생성한다면 Singleton 패턴을 사용하지 않을 수도 있겠지만 확실히 보장 할 수 없다.

즉,

  • 지정한 클래스의 객체가 절대로 1개밖에 존재하지 않는 것을 보증하고 싶을 때
  • 객체가 1개밖에 존재하지 않는 것을 프로그램 상에서 표현하고 싶을 때

Singleton패턴을 사용한다.

자바의 Singleton 패턴 구현 방식

singleton 패턴 구현

  • static 속성을 이용하여 객체를 인스턴스화 하지 않고 사용
  • 객체의 접근 제한자가 private이므로 직접적인 접근 불가능
  • 생성자가 private 으로 선언 되어있으므로 외부에서 new()를 통한 접근 불가능
  • getInstance() 메소드를 통해서만 객체에 접근 가능

Singleton 패턴은 몇가지 방식으로 구현 할 수 있다.

eager Initialization

public class Singleton {
    // Singleton 객체 하나를 생성해서 가리키는 정적 필드
    // 클래스가 메모리에 로드될 때 한번 실행
    private static Singleton makeInstance = new Singleton();

    // private construct , new를 통한 객체 생성 불가
    private Singleton() {}

    // Singleton 객체 반환
    public static Singleton getInstance() {
        return makeInstance;
    }
}

eager Initialization 방식은 Singleton 인스턴스를 미리 생성해 놓는 방식으로, Multi-thread 환경이 아니면서 Singleton 인스턴스가 많은 일을 하지 않을 때 사용 할 수 있다.

Static block

public class Singleton {
    //Instance
    private static Singleton makeInstance;

    //private construct
    private Singleton() {}

    static {
        try { makeInstance = new Singleton();}
        catch(Exception e) { 
	throw new RuntimeException("Create instace fail. error msg = " + e.getMessage() ); }
    }

    public static Singleton getInstance() {
        return makeInstance;
    }
}

Static block 방식은 eager Initialization방식에서 예외처리가 가능해진 형태이다.

Static block : 클래스가 로딩되고 클래스 변수가 준비된 후 자동으로 실행되는 블록

위의 두방식은 클래스가 로딩될 때 실행되며, 객체가 사용되기 이전 메모리를 점유하는 문제가 발생한다.

lazy Initialization

public class Singleton {
	
	private static Singleton makeInstance;

	private Singleton() {}

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

lazy InitializationgetInstance() 메소드가 호출 될 때, 클래스의 인스턴스가 사용되는 시점에 Singleton 객체를 생성한다.

앞선 세가지 방식 모두 Multi-thread환경에서는 동작이 보장되지 못한다.

쓰레드

만약, 위 그림의 상황에서 스레드A스레드B가 동시에 getInstance() 를 호출한 경우, 먼저 cpu를 할당받은 스레드A가 Singleton 객체를 생성하고, makeInstance 변수에 할당하기 직전에, 스레드B가 CPU를 할당받고 getInstance( ) 를 호출한다면, 스레드B 또한 Singleton 객체를 생성 할 것이다.

Lazy Initialization with synchronized

public class Singleton {
    private static Singleton makeInstance;

    private Singleton() {
    }

    public static synchronized Singleton getInstance() {
        if (makeInstance == null) {
            makeInstance = new Singleton();
        }
        return makeInstance;
    }
}

Multi-thread환경에서 Singleton 패턴을 보장하기 위해서 getInstance( ) 메소드를 synchronized로 선언하여 thread에서의 동시 접근에 대한 문제를 해결 할 수 있다. 하지만 위의 방식은 객체 생성의 유무와 관계없이 synchronized block 을 거치게 되어 성능이 크게 저하된다.

Lazy Initialization. Double-Checked Locking(DCL)

public class Singleton {
    private static Singleton makeInstance;

    private Sigleton() {}

    // Lazy Initialization. DCL
    public Singleton getInstance() {
      // instance가 null인 경우에만 synchronized block에 접근
      if(makeInstance == null) {
         synchronized(Singleton.class) {
            if(makeInstance == null) {
               makeInstance = new Singleton(); 
            }
         }
      }
      return makeInstance;
    }
}

DCL 방식객체가 아직 생성되어 있지 않은 경우에만 synchronized block에 접근하므로 성능저하를 보완할 수 있다.

Singleton 패턴의 장점

  • 한번의 생성으로 객체를 사용하기 때문에 고정된 메모리 영역을 얻을 수 있으므로 메모리 낭비를 방지 할 수 있다.

  • Singleton으로 만들어진 객체는 전역 객체이기 때문에 다른 클래스의 객체들이 데이터를 공유하기 쉽다.

Singleton 패턴의 한계

  • Singleton으로 만든 객체의 역할이 복잡한 경우라면 해당 객체를 사용하는 객체간의 상호 의존도가 높아져서 객체 지향 설계 원칙 중 하나인 개방-폐쇄 원칙 에 어긋나게 된다.

개방- 폐쇄 원칙(OCP, Open-Closed Principle) : 소프트 웨어 개체(클래스, 모듈, 함수 등등)는 확장에 대해 열려 있어야 하고, 수정에 대해서는 닫혀있어야 한다는 객체 지향의 설계 원칙

참고

-개방 폐쇄 원칙 OCP (Open-Closed Principle)

  • multi-thread환경에서 동기화 처리 문제가 발생 할 수 있다.

Reference