05. 싱글턴 패턴

Mando·2023년 3월 22일
0

디자인 패턴

목록 보기
4/6

싱글턴 패턴이란?

특정 클래스에 객체 인스턴스가 단 하나만 만들어지도록 해주는 패턴

1. 어떤 곳에 싱글턴 패턴을 사용하는 것일까?

  • 인스턴스가 2개 이상이면 결과에 일관성이 없어져 프로그램이 이상하게 돌아가는 경우 싱글턴 객체를 이용하면 한 애플리케이션에 들어있는 어떤 객체도 같은 자원을 사용할 수 있다.
  • 로그 기록용 객체, 사용자 설정, 스레드 풀 같은 게 있다.

2. 전역 변수를 통해서 단 한 개의 객체만 생성해 사용하게 할 수 있지 않은가?

전역 변수에 객체를 대입하면 애플리케이션이 시작될 때 객체가 생성된다.
그런데 해당 객체가 자원을 많이 잡아먹는다고 가정을 해보자

만약 애플리케이션이 끝날 때까지 그 객체를 한번도 쓰지 않는다면 괜히 자원만 잡아먹는 쓸데없는 객체인 것이다.

만약, 싱글턴 패턴을 사용한다면 필요할 때만 객체를 생성할 수 있다.

고전적인 싱글턴 패턴

어떤 문제점이 있을까?

  • uniqueInstance : Singleton 클래스의 하나뿐인 인스턴스를 저장하는 정적 변수
  • 생성자 : 생성자의 접근제어자는 private이므로 Singleton 클래스 내에서만 클래스의 인스턴스를 만들 수 있다.
  • getInstance() : 싱글턴 객체가 필요할 때 인스턴스를 달라고 요청받는 정적 메서드로 싱글턴 객체를 리턴한다.
 
	private static Singleton uniqueInstance;

	private Singleton() {}

	public static Singleton getInstance() {

		//아직 인스턴스가 생성되지 않았다면
		if (uniqueInstance == null) {
			//인스턴스를 생성하고 정적 변수에 저장한다.
            //즉, 인스턴스를 필요로 하는 시점에 인스턴스를 생성한다 -> 게으른 인스턴스 생성
			uniqueInstance = new Singleton();

		}
		
        //인스턴스가 이미 생성되어 있는 경우는 정적 변수에 저장된 인스턴스를 반환한다.
		return uniqueInstance;

	}
    
    //기타 메서드 

즉 인스턴스가 필요로 한 시점에 정적 메서드로 인스턴스를 달라고 요청하면
1. 인스턴스가 있는 경우 : 정적 변수에 저장된 인스턴스를 리턴
2. 인스턴스가 없는 경우 : 그제서야 인스턴스를 생성하고 정적 변수에 저장

처음부터 정적 변수에 인스턴스를 저장하면 사용하지 않더라도 인스턴스를 생성하여 자원을 잡아먹을 수도 있는데

싱글턴을 사용하면 인스턴스가 필요로 하는 시점에 인스턴스를 생성하기에 자원 낭비가 없다는 장점이 있다.

멀티스레딩 문제가 발생할 수 있다고?

멀티스레딩 문제 해결하기

synchronized 키워드

synchronized 키워드를 이용하면 한 스레드가 메서드 사용을 끝내기 전까지 다른 스레드는 기다려야 한다.

즉, 2개의 스레드가 해당 정적 메서드를 동시에 실행할 수가 없다.

동기화가 필요한 시점이 정확히 어디일까?

동기화가 필요한 시점은 인스턴스를 생성하는 것 뿐이다.
이후, 생성된 인스턴스를 사용하는 시점은 동기화된 상태가 필요하지 않다.
즉 처음을 제외하면 동기화는 불필요한 오버헤드만 증가시킬 뿐이다.

public class Singleton{
 
	private static Singleton uniqueInstance;

	private Singleton() {}

	public static synchronized Singleton getInstance() {

		//아직 인스턴스가 생성되지 않았다면
		if (uniqueInstance == null) {
			//인스턴스를 생성하고 정적 변수에 저장한다.
            //즉, 인스턴스를 필요로 하는 시점에 인스턴스를 생성한다 -> 게으른 인스턴스 생성
			uniqueInstance = new Singleton();

		}
		
        //인스턴스가 이미 생성되어 있는 경우는 정적 변수에 저장된 인스턴스를 반환한다.
		return uniqueInstance;

	}
    
    //기타 메서드 
 }

효과적으로 멀티스레딩 문제 해결하기

방법 1. getInstance의 속도가 중요하지 않다면 그냥 둔다

성능 문제가 발생한다.
성능이 약 100배 정도 저하된다고 한다.

따라서 getInstance()가 애플리케이션에서 병목으로 작용한다면 다른 방법을 생각해봐야 한다.

방법 2. 인스턴스가 처음부터 필요하다면 처음부터 생성한다.

애플리케이션에서 반드시 Singleton 인스턴스를 생성하고, 그 인스턴스를 항상 사용한다면 처음부터 Singleton 인스턴스를 만드는 것도 좋다.

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

public class Singleton {

	private static Singleton uniqueInstance = new Singleton();

	private Singleton() {}

	public static synchronized Singleton getInstance() {

		return uniqueInstance;

	}

}

방법 3. DDL을 써서 getInstance()에서 동기화되는 부분을 줄인다.

public class 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;

	}

}

0개의 댓글