관리 메뉴

nalaolla

[디자인패턴]싱글턴 패턴(Singleton Pattern) 본문

JAVA/999. 디자인패턴

[디자인패턴]싱글턴 패턴(Singleton Pattern)

날아올라↗↗ 2020. 4. 9. 17:01
728x90

 

싱글턴 패턴(Singleton Pattern) = 해당 클래스의 인스턴스가 하나만 만들어지고, 어디서든지 그 인스턴스에 접근할 수 있도록 하기 위한 패턴. 

 

싱글턴 객체는 유일하게 존재 하는 객체입니다. 예를 들면, 레지스트리 설정이라던가, DB 커넥션 객체 등이 여러 개 존재하면 심각한 오류가 발생하거나, 자원이 낭비 될 가능성이 있죠. 

 

(예를 들어서 2개의 설정 객체의 인스턴스를 무작위로 참조 하게 된다면.. )

 

이런 객체들은 단 하나만 존재하게 해서, 그 객체의 인스턴스만 어디서든 쓸 수 있게 만들어 줘야 합니다. 

 

싱글턴 객체가 이 싱글턴 객체를 어떻게 만들까요?

 

싱글턴 객체를 만드려면 두 가지의 조건을 만족해야 합니다.

 

1. 어디서든 접근이 가능하고, 인스턴스를 얻어서 사용할 수 있게 할 것.

2. 객체는 단 하나만이 메모리에 할당 되어 있을 것.

 

 

이런 조건들을 살펴 보았을때, 인스턴스를 얻는 getInstance()라는 정적 메소드를 정의 하는 것이 좋아 보입니다. 그래서 이렇게 코드를 작성할 수 있죠.

 

public class Singleton{

    

    private static Singleton uniqueInstance; // 유일한 변수를 사용하기 위해 정적 변수로 선언

    

    private Singleton() {}

    

    public static Singleton getInstance() {

        

        if(uniqueInstance == null) {

            

            uniqueInstance = new Singleton();

        } //필요할때만 객체를 생성하는 방식. 게으른 인스턴스 생성(lazy instantiation)

        

        return uniqueInstance;

        

    }

    

 

}

Colored by Color Scripter

 

 

이렇게 되면 변수와 생성자는 private으로 선언되어 있기 때문에 아무도 건드릴 수 없고, 오직 getInstance() 메소드를 통해 이 객체에 접근할 수 있습니다.

 

이렇게 작성 된 싱글턴 패턴은 단일 스레드 환경에서는 별 문제가 없지만, 다중 스레드 환경에서는 심각한 문제를 초래할 수 있습니다. 

 

스레드는 프로세스와 같이 스케줄링에 의한 문맥교환(context switching)이 발생합니다. 이렇게 되면 2개의 객체가 생성될 수 있습니다. 

 

이 문제를 해결하려면 세 가지의 방법이 있습니다.

 

1. synchronized , 동기화를 사용한다. 

2. 인스턴스를 필요할 때 생성하지 말고, 아예 처음부터 정적 객체로 생성한다. 

3. DCL (Double-Checking Locking)을 사용한다. 

 

 

1. 동기화를 이용한 방법

 

public class Singleton{

    

    private static Singleton uniqueInstance; // 유일한 변수를 사용하기 위해 정적 변수로 선언

    

    private Singleton() {}

    

    public static synchronized Singleton getInstance() { // 동기화 처리

        

        if(uniqueInstance == null) {

            

            uniqueInstance = new Singleton();

        } //필요할때만 객체를 생성하는 방식. 게으른 인스턴스 생성(lazy instantiation)

        

        return uniqueInstance;

        

    }

    

 

}

Colored by Color Scripter

 

--> 이 방법을 사용하면 간단하지만, 객체가 한 번 생성되면 더이상 생성 시킬 필요가 없는데도 불구하고 계속 동기화 처리를 하여 성능 저하가 일어난다는 단점이 있습니다. 약 100배의 속도 저하가 일어납니다.

 

 

2. 아예 처음부터 생성하는 방법

 

public class Singleton{

    

    private static Singleton uniqueInstance = new Singleton(); // 생성과 동시에 초기화

    

    private Singleton() {}

    

    public static Singleton getInstance() {

        

        return uniqueInstance;

        

    }

    

 

}

 

Colored by Color Scripter

 

--> 이렇게 되면 스레드의 영향을 받지 않고 유일한 객체를 생성할 수 있게 됩니다.

 

3. DCL 이용하는 방법

 

public class Singleton {

 

    private volatile Singleton uniqueInstance; 

 

    private Singleton() {

    }

 

    public static Singleton getInstance() {

 

        if (uniqueInstance == null) {

            synchronized (Singleton.class) {

                if (uniqueInstance == null) {

                    uniqueInstance = new Singleton();

                }

            }

        }

        return uniqueInstance;

    }

 

}

Colored by Color Scripter

 

 

volatile 예약어에 대한 설명 <-- 보러가기

 

--> 이 방식을 사용하면 synchronized만 사용할때 보다 오버헤드를 매우 낮출 수 있으나 자바 1.4 이하 버전에서는 volatile 예약어를 지원하지 않습니다.

728x90