[Java] 싱글톤 패턴 (Singleton Pattern)
Spring

[Java] 싱글톤 패턴 (Singleton Pattern)

728x90

싱글톤 패턴을 따르지 않은 나루토

세상에 무한으로 존재하는 자원은 없습니다. 컴퓨터도 마찬가지죠. 가비지 컬렉션(GC)편에서도 볼 수 있듯 이러한 한정된 메모리를 최대한 효율적으로 사용하는 방법은 여러가지가 있습니다. new MyClass() 같은 코드로 객체를 새롭게 생성을 하면 새로운 메모리 공간에 해당 객체를 위한 정보가 쓰이게 되겠죠. 하지만 하나의 객체만 생성해서 계속해서 재사용하면 어떨까요?

 

바로 이처럼 싱글턴 패턴은 하나의 객체만을 생성해서 이후에는 처음에 생성된 객체를 반환하여 프로그램 전반에서 하나의 인스턴스만을 사용하게 하는 패턴입니다.

 


싱글톤 패턴 적용 예제

public class Test {
    public static void main(String[] args){
        DannySingletonClass singletonClass1 = DannySingletonClass.getInstance();
        DannySingletonClass singletonClass2 = DannySingletonClass.getInstance();
        DannySingletonClass singletonClass3 = DannySingletonClass.getInstance();

        System.out.println(singletonClass1.s);
        System.out.println(singletonClass2.s);
        System.out.println(singletonClass3.s);
        System.out.println();

        singletonClass1.s = "hahaha";

        System.out.println(singletonClass1.s);
        System.out.println(singletonClass2.s);
        System.out.println(singletonClass3.s);
    }
}

class DannySingletonClass {
    private static DannySingletonClass dannySingletonClass = null;

    public String s;

    private DannySingletonClass() {
        s = "hello first";
    }

    public static DannySingletonClass getInstance() {
        if(dannySingletonClass == null) {
            dannySingletonClass = new DannySingletonClass();
        }
        return dannySingletonClass;
    }
}

위 예제는 싱글톤 패턴이 적용된 클래스를 생성하는 예제입니다.

DannySingletonClass를 보시면 몇가지 특징이 존재하죠.

  • 생성자가 private 으로 되어있다.
  • 따라서 생성자로 생성을 못하고 getInstance() 메소드를 호출해서만 생성할 수 있다.
  • getInstance() 메소드를 호출한다고해도 만약 한번 생성이 됐다면, 이미 생성된 객체를 반환한다

말그대로 딱 하나의 객체만 생성할 수 있는거죠. 그래서 위 코드를 실행하면 마치 첫번째 객체만 바뀌어서 아래처럼  나올것같지만

hello first
hello first
hello first

hahaha
hello first
hello first

실제로는 아래처럼 나옵니다.

hello first
hello first
hello first

hahaha
hahaha
hahaha

싱글톤 패턴의 문제점

와 그럼 하나 생성해서 하니까 메모리가 엄청 세이브되겠네요?? 무조건 이렇게 해야겠네?? 라고 하기에는 문제점과 고려해야할 요소가 여러가지 존재합니다. 바로 Multi-Thread 환경에서 안전하지 않기 때문인데요.

 

1. 인스턴스가 여러개 생길 수 있는 문제

여러 쓰레드가 공유되고 있는 상황에서는 아래의 블럭에서 조건문이 동시에 두번 돌 수 있기때문에 하나의 인스턴스가 아닌 여러개의 인스턴스가 발생 할 위험이 있습니다.

public static DannySingletonClass getInstance() {
    if(dannySingletonClass == null) {
        dannySingletonClass = new DannySingletonClass();
    }
    return dannySingletonClass;
}

2. 변수값 변화의 일관성

또한 인스턴스가 상태유지를 해야하는 상황에서는 여러 쓰레드에서 변수값을 공유하고 있기 때문에 값이 일관되지 않을 수있습니다. 아래 예제를 볼까요?

class DannySingletonClass {
    private static DannySingletonClass dannySingletonClass = null;
    private int count = 0;

    private DannySingletonClass() {}

    public static DannySingletonClass getInstance() {
        if(dannySingletonClass == null) {
            dannySingletonClass = new DannySingletonClass();
        }
        return dannySingletonClass;
    }
    
    public void increaseCount() {
        count++;
    }
}

여러 스레드에서 동시에 increaseCount() 메소드를 호출한다면 쓰레드마다 count 값이 어떻게 변화가 될지 결과값을 알 수 없겠죠?

 

위 문제를 해결한 코드

class DannySingletonClass {
    private static final DannySingletonClass dannySingletonClass = new DannySingletonClass();
    private static int count = 0;

    private DannySingletonClass() {}

    public static DannySingletonClass getInstance() {
        return dannySingletonClass;
    }

    public synchronized void increaseCount() {
        count++;
    }
}

정적 변수는 객체가 생성되기 전 클래스가 메모리에 로딩할 때 만들어져 초기화가 한 번만 실행됩니다. 따라서 정적 변수에 클래스를 초기화해놓으면 1번 문제가 해결됩니다. 그리고 synchronized 를 메소드에 포함되어 여러 쓰레드에서 동시에 접근하는 것을 막아 2번 문제를 해결할 수 있습니다.


결론

  • 싱글톤은 프로그램 전체에서 하나의 객체만을 공통으로 사용하고 있기 때문에 각 객체간의 결합도가 높아지고 변경에 유연하게 대처할 수 없습니다.
  • 멀티쓰레드 환경에서 대처가 어느정도 가능하지만 고려해야 할 요소가 많아 사용이 어렵고, 프로그램 전반에 걸쳐서 필요한 부분에만 사용한다면 장점이 있습니다. 하지만 그 포인트를 잡기가 어렵습니다

 

출처:
- 자바 싱글톤 패턴 (Singleton Pattern)
- 자바 싱글톤 클래스(Singleton class)
728x90