자바 디자인패턴 - 싱글톤 패턴

유사개발자·2022년 8월 3일
0

Design Patten

목록 보기
1/3
post-thumbnail

싱글톤 패턴

  • 인스턴스를 오직 한개만 제공하는 클래스
  • 인스턴스가 여러개 일 때 문제가 생길 수 있는 경우가 있다.
  • 이때 오직 한개의 인스턴스만 제공하는 클래스가 필요하다.

싱글톤 클래스 생성법

생성자를 private으로 선언하여 클래스 밖에서 인스턴스 생성을 막는다.

  • 가장 기초적인 싱글톤 구현법이지만 문제점이 있다
    ( 멀티스레드 환경에서 안전하지 않음 )
public class Settings {

    private static Settings instance;

    private Settings() {}

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

만약 1번스레드가 if문을 타고 인스턴스 생성을 완료하지 못했는데 2번 스레드도 if문을 탄다면
아직 인스턴스가 생성되지 않았기 때문에 2번 스레드도 new Settings()로 객체를 생성하게 됨

멀티스레드 환경에서 안전한 싱글톤 패턴 코드

1. synchronized 키워드 사용하기

(가장 간편한 방법이지만 성능상에 불이득이 생길 수 있음)

  • 단점 -> 동기화 작업때문에 lock -> unlock으로 인한 성능 저하가 있을 수 있음
public class Settings {

    private static Settings instance;

    private Settings() {}

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

2. 이른 초기화 사용하기

(메소드에 여러 쓰레드가 들어와서 작업하더라도
이미 생성된 instance를 반환하기 때문에 쓰레드 safe하다)

  • 단점 -> 안쓰는 객체를 큰 비용을 들여 생성해야 할 수도 있음
public class Settings {

    private static final Settings INSTANCE = new Settings();

    private Settings() {}

    public static Settings getInstance() {
        return INSTANCE;
    }
}

3. double checked locking 사용하기

(매우 많은 트래픽 발생시 if문안에 여러 쓰레드가 동시에 들어올 수 있는데 그럴 경우에만 synchronized하게 효율적으로 처리, 이른 초기화와 달리 객체 사용시점에 인스턴스 생성 )

  • 단점 -> 코드 자체도 복잡하고 자바 1.5버전 이상부터 사용가능한 코드임
public class Settings {

    private static volatile Settings instance;

    private Settings() {}

    public static Settings getInstance() {
        if(instance == null) {
            synchronized (Settings.class) {
                if(instance == null) {
                    instance = new Settings();
                }
            }
        }
        return instance;
    }
}

4. static inner 클래스 사용하기

(getInstance() 가 호출될때 SettingHolder가 로딩되기 때문에 지연로딩, 스레딩 safe함)
#권장되는 방법중 하나

public class Settings {

    private Settings() {}

    private static class SettingHolder {
        private static final Settings INSTANCE = new Settings();
    }

    public static Settings getInstance() {
        return SettingHolder.INSTANCE;
    }
    
    private Object readResolve() {
        return getInstance();
    }
}

하지만 리플렉션이나 직렬화(객체를 파일로저장), 역직렬화(파일을 객체로변환)시 싱글톤이 깨질 수 있음

직렬화, 역직렬화시엔 readResolve() 라는 메소드가 쓰이는데 이때 new Settings()를하여 싱글톤이 깨진다

  • 해결방안 -> 싱글톤 클래스에 readResolve() 메소드를 재정의하여
    holder방식의 static inner 클래스를 만든 후 getInstance()메소드를 사용 시 만들어진 인스턴스를 사용하여 막을 수 있음

5. enum 클래스 사용하기

(enum 클래스는 리플렉션 사용 시 예외를 던지고 Serializable가 구현되어있으며
직렬화, 역직렬화 시에도 같은 인스턴스 보장함)

  • 단점 -> 즉시로딩이기 때문에 인스턴스 생성 과정에서 비용이 많이든다면 비효율적이다.
    또한 enum 클래스만 상속 받을 수 있다.
public enum Settings {
	INSTANCE;
}

만약 싱글톤 패턴을 구현하는데 있어서 상속을 받아야 하거나 즉시로딩이 아닌 지연로딩이 적합하다 판단된다면 holder방식의 static inner class방식을 사용하는 것이 적합하다.

느낀점

싱글톤의 개념만 이해했지만 다른 여러방식을 통해 멀티스레드 환경 및 싱글톤이 깨지는 경우에 어떻게 대처해야 하는지 알 수 있었고, static inner class의 실행시점이 신기했다

이 부분은 jvm을 공부해서 포스팅 해야겠다.

출처 : 인프런 백기선 - 디자인 패턴

profile
개발자와 유사한 개발자입니다

0개의 댓글