노트코드
노트코드
노트코드

블로그 메뉴

  • 홈
  • 이력서
  • 이전 블로그
  • 글쓰기
  • 관리자페이지
  • 분류 전체보기 (57)
    • 코틀린 (2)
      • 실무 프로젝트로 배우는 Kotlin & Sprin.. (2)
    • JAVA (1)
      • 디자인패턴 (1)
      • 객체지향 5대원칙 (0)
    • SPRING (32)
      • JPA (11)
      • 스프링시큐리티 (1)
      • 스프링 (8)
      • QueryDsl (1)
      • 스프링배치 (11)
    • AZURE (0)
    • ETC (10)
      • MAVEN (0)
      • GIT (0)
      • ReMind (3)
      • Exception (1)
      • CS (6)
    • 책 (8)
      • 이것이 자바다 (8)

최근 글

최근 댓글

태그

  • JPA
  • 스프링
전체 방문자
오늘
어제
hELLO · Designed By 정상우.
노트코드

노트코드

JAVA/디자인패턴

[백기선-코딩으로 학습하는 GOF디자인] 1. 싱글톤 패턴

2022. 2. 9. 13:46

싱글톤 패턴

구현방법

1.

public class Settings {

    private static  Settings instance;

    public Settings() {
    }

    public static Settings getInstance(){
        if(instance == null){
            instance = new Settings();
        }
        return instance;
    }
}
  • 두개의 요청중 첫번째 요청에서 new Setting을 통해 인스턴스를 만들기전
    또 다른 요청이 들어오면 첫번째 요청에서 생성되기 직전이라 하나를 더 만들게된다.(쓰레드세입하지 않음)

2.

public class Settings {
    ...
    public static synchronized Settings getInstance(){
        if(instance == null){
            instance = new Settings();
        }
        return instance;
    }
}
  • 해결방법 : synchronized를 사용해주면된다.
  • 또다른 문제: 하나의 요청을 만들때 락이 걸리면서 다른 요청은 먼저 들어온 요청이 끝날때까지 기다리게되고, 성능 이슈가 발생

3. 이른 초기화 사용

public class Settings {

    private static final Settings INSTANCE = new Settings();

    public Settings() {
    }

    public static Settings getInstance() {
        return INSTANCE;
    }
}
  • 쓰레드 safe함, 몇번의 요청이 들어와도 이미 만들어져있기 때문에
  • 단점: 미리 만든다는 자체, 인스턴스를 만드는데 굉장히 오래걸리고, 많은 메모리를 잡아먹는다면 효율적이지 않음

4. double checked locking 사용하기

public class Settings {

    private static volatile Settings INSTANCE;

    public Settings() {
    }

    public static Settings getInstance() {
        if (INSTANCE == null) {
            synchronized (Settings.class) {
                if (INSTANCE == null) {
                    INSTANCE = new Settings();
                }
            }
        }
        return INSTANCE;
    }
}
  • 두개의 요청중 첫번째 요청 처리중 또다른 요청이 들어와도, synchronized로 인해 중복으로 생성될 가능성이 없음,
  • volatile를 써줘야하는데 1.4버전 이후에서만 동작하고, 이것을 사용하려면 1.4버전에서 멀티쓰레드가 메모리를 어떻게 공유하는지에 대해 깊게 알아야함

5. static inner 클래스 사용하기

public class Settings {

    private Settings() {
    }

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

    public static Settings getInstance() {
        return SettingsHolder.INSTANCE;
    }
}
  • 클래스 로딩 : 클래스에 static 필드가 있고 static 필드에 대한 접근이 이루어지면 클래스가 로드된다.
  • BillPughSingleton 클래스에도 doNothing() 이라는 아무것도 하지 않는 static 메소드를 선언하고, 이에대한 호출을 하면 BillPughSingleton 클래스가 로드되는 것을 확인할 수 있다. (jvm 옵션에 -verbose:class 주고 실행) 이때 doNothing() 만 호출해서는 SingletonHelper 클래스는 로드되지 않는 것도 확인할 수 있다. 만약 getInstance() 호출로 SingletonHelper 클래스에 대한 사용이 이루어지는 코드를 삽입하면 SingletonHelper 클래스가 로드되는 것을 확인할 수 있다.
  • 참고 : https://yaboong.github.io/design-pattern/2018/09/28/thread-safe-singleton-patterns/

6. 싱글톤 패턴 구현 방법을 깨뜨리는 방법

  • 리플렉션 사용
public class App {
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Settings settings = Settings.getInstance();
        Constructor<Settings> constructor = Settings.class.getDeclaredConstructor();
        constructor.setAccessible(true);
        Settings settings1 = constructor.newInstance();
        System.out.println(settings == settings1);
    }
}

 

직렬화 & 역직렬화

  public class Settings implements Serializable {
      ///
  }
  public class App {
      public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, IOException, ClassNotFoundException {
          Settings settings = Settings.getInstance();
          Settings settings1 = null;

          //직렬화
          ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("settings.obj"));
          out.writeObject(settings);

          //역직렬화

          try (ObjectInputStream in = new ObjectInputStream(new FileInputStream("settings.obj"))) {
              settings1 = (Settings) in.readObject();
          }

          System.out.println(settings == settings1);

      }
  }

직렬화 역직렬화 막는 방법 : 역직렬화할때 readResolve()라는 메소드를 사용하게 되는데,

아래와 같이 작성하여 getInstance를 전달해주면됨

역직렬화 할때 생성자를 만들어 값을 넣어주게 되는데, 그것을 활용하여 코드를 작성

  public class Settings implements Serializable {

      private Settings() {
      }

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

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

      protected Object readResolve() {
          return getInstance();
      }
  }

7. 안전하고 단순하게 구현하는 방법

enum (enum은 Serialize를 구현하고 있으며, Reflaction을 자체적으로 막아 놓고 있다.)

public enum Settings {
    INSTANCE;

    Settings() {
    }

    private Integer number;

    public Integer getNumber() {
        return number;
    }

    public void setNumber(Integer number) {
        this.number = number;
    }
}

단점: 미리 생성한다는점

8. 자바와 스프링에서 찾아보는 패턴

  • 스프링에서 빈 스코프 중에 하나로 싱글톤 스코프
  • java.lang.Runtime
  • 다른 디자인 패턴(빌더, 퍼사드, 추상 팩토리등) 구현체의 일부로도 사용됨
    노트코드
    노트코드
    노션 블로그에서 티스토리로 이전공사중

    티스토리툴바