ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 싱글턴 패턴
    컴퓨터/객체지향_디자인패턴 2019. 9. 3. 23:24

    - 싱글턴이란?

     특정 클래스에 대해서 객체 인스턴스가 하나만, 필요할 때만! 만들어질 수 있도록 해주는 패턴

    어디서든지 그 인스턴스에 접근할 수 있도록 하기 위한 패턴

    Singleton.getInstance() 와 같은 형태 - 어디서든 무조건 같은 인스턴스가 리턴

    제한된 용도로 특수한 상황에서 사용!

     

    - 사용예

    스레드 풀, 캐시, 대화상자, 사용자 설정, 레지스트리 설정을 처리하는 객체, 로그 기록용 객체, 프린터나 그래픽 카드 같은 디바이스를 위한 디바이스 드라이버

    -> 인스턴스를 두 개 이상 만들게 되면 프로그램이 이상하게 돌아간다든가 자원을 불필요하게 잡아먹는 경우, 결과에 일관성이 없어지는 경우

     

    - 필요성

    1) 전역변수의 단점

    : 애플리케이션이 시작될 때 객체 생성, 그 객체가 자원을 많이 차지하는데 사용하지 않는다면 자원만 잡아먹는다. 

    싱클턴은 필요할 때만 객체를 만들 수 있다. (lazy instantiation)

     

     

    - 어떻게 하면 한 클래스의 인스턴스가 두 개 이상 만들어지지 않도록 할 수 있을까?

    정적 클래스 변수와 메소드, 접근 변경자를 잘 다룰 줄 알아야

    [고전적인 싱글턴 패턴 구현법]

    public class Singleton {
    	// Singleton 클래스의 유일한 인스턴스를 저장하기 위한 정적 변수
        private static Singleton uniqueInstance;
        
        // 기타 인스턴스 변수
        // 생성자를 private으로 선언했기 때문에 singleton에서만 클래스의 인스턴스를 만들 수 있다.
        private Singleton() {}
        
        // class의 instance를 만들어서 반환해준다
        public static synchronized Singleton getInstance() {
        	if (uniqueInstance == null) {
            	uniqueInstance = new Singleton();
            }
            return uniqueInstance;
        }
        
        // 기타 메서드
    }

    생성자가 private으로 구현되어 있다. 

    클래스에 인스턴스를 달라고 요청해야 한다.

     

    - 특징

    1) 전역 변수와 마찬가지로 객체 인스턴스를 어디서든지 접근할 수 있다.

    그러나 전역 변수는 객체에 대한 정적 레퍼런스이고 레퍼런스를 자꾸 만들게 되면서 네임스페이스를 지저분하게 만들 수 있다.

     

    2) 클래스에서 인스턴스 관리

     

    3) 정적 변수를 바탕으로 구현 -> 서브 클래스를 만들면 모든 서브클래스들이 똑같은 인스턴스 변수를 공유한다.

    따라서 베이스 클래스에서 레지스트리 같은 걸 구현해야 한다

     

    - 주의 1: 클래스 로더가 여러 개 있으면 싱글턴이 제대로 작동하지 않고 여러 개의 인스턴스가 생길 수 있다.

    - 주의 2: 멀티스레딩 문제 : 스레드가 추가되면 객체가 여러 개 생성될 수 있다.

    how to solve?

    1) synchronized로 동기화시킨다

    public static synchronized 클래스명 메서드명() {}

    한 스레드가 메소드 사용을 끝내기 전까지 다른 스레드는 기다려야 한다.

    두 스레드가 해당 메서드를 동시에 실행하는 일이 없다.

     

    이 방법의 단점 : 일단 uniqueInstance 변수에 인스턴스가 대입되면 동기화된 상태를 유지할 필요가 없다.

    동기화하면 성능이 100배 정도 저하된다. 

     

    2) 인스턴스를 처음부터 만들어버리기

    public class Singleton {
    	// 정적 초기화부분(static initializer)에서 인스턴스를 생성한다.
        private static Singleton uniqueInstance = new Singleton();
        
        private Singleton() {}
        
        public static Singleton getInstance() {
        	return uniqueInstance;
        }
        
    }

    클래스가 로딩될 때 JVM에서 싱글턴의 유일한 인스턴스를 생성해준다.

     

    3) DCL(Double-checking Locking)을 써서 getInstance()에서 동기화되는 부분을 줄인다.

    인스턴스가 생성되어 있는지 확인하고 생성되어 있지 않았을 때만 동기화할 수 있다.

    처음에만 동기화 가능

    public class Singleton {
        // volatile : 멀티스레딩을 쓰더라도 uniqueInstance 변수가 Singleton 인스턴스로
        // 올바른 과정으로 초기화할 수 있어
        private volatile static Singleton uniqueInstance;
    	
        private Singleton () {}
        
        public static Singleton getInstance() {
        	if (uniqueInstance == null) {
            	synchronized (Singleton.class) {
                	if (uniqueInstance == null) {
                    	uniqueInstance = new Singleton();
                    }
                }
            }
            return uniqueInstance;
        }
    }

     

    - 주의 3 : 1.2 버전보다 전에 나온 JVM을 사용하는 경우에는 가비지 컬렉터 관련 버그 때문에 싱글턴 레지스트리를 사용해야 할 수도 있다.

     

    출처 : headfirst design pattern

    '컴퓨터 > 객체지향_디자인패턴' 카테고리의 다른 글

    디자인 원칙 : OCP  (0) 2019.09.07
    옵저버 패턴  (0) 2019.09.07
    데이터를 화면에 표시하는 방법 : MVC 패턴  (0) 2019.09.07
    플라이웨이트 패턴  (0) 2019.09.03

    댓글

Designed by Tistory.