본문 바로가기
개발/Java & Kotlin

싱글톤(Singleton) 패턴이란?

by 남트로 2023. 5. 2.

오늘은 싱글톤 패턴에 대해서 알아볼 것이다.

 

싱글톤패턴은 개발하면서 많이 들어보는 용어중 하나인데 개선된 코드를 짜기 위해 상황에 맞게짜면 굉장히 좋은 패턴이다.

 

같이 한번 알아보도록 하자

 

싱글톤(Singleton) 패턴 정의


싱글톤 패턴이란

"어떤 클래스의 인스턴스는 오직 하나임을 보장하며, 이 인스턴스에 접근할 수 있는 전역적인 접촉점을 제공하는 패턴이다."

라고 정의되어 있다.

 

 싱글톤(Singleton)은 앱의 시작부터 종료될 때 까지 한번의 생성으로 고정된 메모리영역을 가지기 때문에 메모리를 효율적으로 사용 할 수 있다.

 

또한 싱글톤의 인스턴스(instance)는 전역적으로 사용되므로 다른 클래스의 인스턴스들이 데이터를 공유 변경이 가능하다는 장점을 갖고 있다.

 

예시를 들어보면 가위라는 클래스가 있을 때 필요한 사람이 있을때마다 한사람당 한개씩 가위를 생성하는 것은 비효율적이다.

 

그리고 동일한 사람이 쓰더라도 그 사람이 필요할 때마다 또 생성해야 하기에 그렇게 되면 가위가 넘쳐날 것이다.

 

이러한 비효율적인 현상을 방지하기 위해 가위라는 클래스를 한번 객체화 하면 누가 사용하더라도 처음 한번 객체화한 이 클래스를 사용한다면 효율적이게 된다.

 

싱글톤 패턴은 이와 같다고 보면 된다.



싱글톤 패턴의 대표적인 4가지 방식


싱글톤 패턴에는 대표적으로 4가지 방식이 있다.

Eager Initialization (이른 초기화 방식)

싱글톤 객체를 instance라는 변수로 미리 생성해놓고 사용하는 방식이다.

public class Singleton {
    private static final Singleton instance = new Singleton();

    private Singleton() {}

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

이른 초기화 방식을 사용하게 되면

  • static으로 생성된 변수에 싱글톤 객체를 선언했기에 클래스 로더에 의해 클래스가 로딩될 때 싱글톤 객체가 생성된다.
  • 클래스 로더에 의해 클래스가 최초 로딩 될 때 객체가 생성됨으로 Thread-safe하다.

라는 장점을 가지고 있다. 반면 단점으로는

  • 싱글톤 객체를 사용하든 안하든 해당 클래스가 로딩 되는 시점에 항상 싱글톤 객체가 생성(new) 되고 메모리를 차지하고 있으니 비효율적인 방법이 될 수 있다.

 

 

Lazy Initialization (늦은 초기화 방식)

 

클래스의 인스턴스가 사용되는 시점에서 싱글톤 객체 생성하는 방식이다.

지난번 포스팅에서 소개했던 object의 특징이기도 하다.

public class Singleton {
    private static Singleton instance;
    private Singleton() {  }

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

장점으로는

  • 싱글톤 객체가 필요할 때 인스턴스를 얻을 수 있다. (Eager initialization 방식에 단점을 보완)

반면 단점으로는

  • multi-thread 환경에서 여러 곳에서 동시에 getInstance()를 호출할 경우 인스턴스가 두번 생성될 여지가 있다. (동기화 문제)

Initialization on demand holder idiom (holder에 의한 초기화 방식)

 

 

이 방식은 클래스안에 클래스(Holder)를 두어 JVM의 Class Loader 매커니즘과 Class가 로드되는 시점을 이용한 방식이다.

public class Singleton {
        // Private constructor prevents instantiation from other classes
        private Singleton() { }

        /**
        * SingletonHolder is loaded on the first execution of Singleton.getInstance() 
        * or the first access to SingletonHolder.INSTANCE, not before.
        */
        private static class SingletonHolder { 
                public static final Singleton INSTANCE = new Singleton();
        }
        public static Singleton getInstance() {
                return SingletonHolder.INSTANCE;
        }
}

Lazy initialization 장점을 가져가면서 Thread간 동기화문제를 동시에 해결한 방법이고 가장 많이 사용되고 있다고 한다.

 

Enum Initialization (Enum 초기화 방식)

 

모든 Enum type은 프로그램 내에서 한번 초기화 되는 점을 이용하는 방식이다.

public enum Singleton {
        INSTANCE;
        public void execute (String arg) {
                //... perform operation here ...
        }
} 

싱글톤의 특징(단 한번의 인스턴스 호출, Thread간 동기화) 을 가지며 비교적 간편하게 사용할 수 있는 방법이라고 한다.

댓글