https://jeong-pro.tistory.com/86

 

싱글톤 패턴(Singleton pattern)을 쓰는 이유와 문제점

싱글톤 패턴(Singleton Pattern) 싱글톤 패턴 애플리케이션이 시작될 때 어떤 클래스가 최초 한번만 메모리를 할당하고(Static) 그 메모리에 인스턴스를 만들어 사용하는 디자인패턴. 생성자가 여러 ��

jeong-pro.tistory.com

해당 게시물이 아주 잘 설명하고 있다.

그런데 세 번째, 내부 holder 클래스를 사용하는 부분에서 좀 더 설명이 필요할 것 같아 정리한다.

 

더불어, synchronized에 대해서도 첨언하려고 한다.

 

일단, synchronized는 공유 데이터를 사용하는 코드 영역을 임계 영역으로 지정해놓고, 공유 데이터(객체)가 가지고 있는 lock을 획득한 단 하나의 스레드만 이 영역 내의 코드를 수행할 수 있게 한다. 코드의 수행을 마치고 벗어나 lock을 반납해야만 다른 스레드가 lock을 얻어 임계 영역의 코드를 다시 수행 가능해진다.

 

이처럼 한 스레드가 진행 중인 작업을 다른 스레드가 간섭하지 못하도록 막는 것을 스레드의 동기화라고 한다.

이 동기화를 하는 방법 중 가장 간단한 방법이 synchronized이다. 임계 영역을 설정하는 데 사용되는 키워드이다.

이때 임계 영역을 지정하는 방법이 2가지 존재한다.

 

1. 메서드 전체를 임계 영역으로 지정

public synchronized void Check() {
	// ... 코드;
}

2. 특정 영역을 임계 영역으로 지정

메서드 내 코드 일부를 블럭{}으로 감싸고 블럭 앞에 'synchronized(참조변수)'를 붙이면 된다.

이때 참조변수는 락을 걸고자 하는 객체를 참조하는 것이어야 한다.

 

*임계 영역은 멀티스레드 프로그램의 성능을 좌우하기 때문에

가능하면 메서드 전체보다는 synchronized 블럭으로 임계 영역을 최소화해야 한다.

 

 

그런데, 이 synchronized를 사용하는 것 자체가 성능상 이슈를 발생시키므로 다른 방법이 고안되었다.

바로 내부 holder 클래스를 사용하는 방법이다.

 

3. Initialization on demand holder idiom (holder에 의한 초기화)

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

1

2

3

4

5

6

7

8

9

10

11

12

public class Something {

    private Something() {

    }

 

    private static class LazyHolder {

        public static final Something INSTANCE = new Something();

    }

 

    public static Something getInstance() {

        return LazyHolder.INSTANCE;

    }

}

Colored by Color Scripter

cs

개발자가 직접 동기화 문제에 대해 코드를 작성하고 문제를 회피하려 한다면 프로그램 구조가 그 만큼 복잡해지고 비용 문제가 생길 수 있고 특히 정확하지 못한 경우가 많다.(100%가 아닐수 있음)

그런데 이 방법은 JVM의 클래스 초기화 과정에서 보장되는 원자적 특성을 이용하여 싱글턴의 초기화 문제에 대한 책임을 JVM에 떠넘긴다.

holder안에 선언된 instance가 static이기 때문에 클래스 로딩시점에 한번만 호출될 것이며 final을 사용해 다시 값이 할당되지 않도록 만든 방법.

* 가장 많이 사용하고 일반적인 Singleton 클래스 사용 방법이다.


좀더 설명해보자면, JVM의 클래스로더는 어떤 클래스가 사용되는 시점에 static 멤버들을 로딩한다.

그러나 내부 클래스도 클래스인지라, 외부 클래스의 메서드에서 내부 클래스를 참조하지 않는 한 내부 클래스는 로딩되지 않는다. 이 원리를 이용해 인스턴스가 필요한 시점에 getInstance() 메서드로 내부 holder 클래스에 접근하고, 그때 홀더 클래스 안의 싱글톤 객체가 static으로 메모리가 로딩되면서 동시에 final로 초기화해주고 바로 객체가 생성된다. final이기 때문에 다시 값이 할당될 일이 없으므로, 다시 getInstance() 메서드가 호출되는 경우 객체 생성 과정은 건너뛰고 만들어져 있던 LazyHolder.INSTANCE; 객체가 반환된다.

'개발자 지식 > 자바' 카테고리의 다른 글

[java] enum 클래스 사용법  (0) 2020.10.14