이 item의 내용들은 java를 싱글톤으로 만드는 좋은 방법에 대한 제안이다.
싱글톤 패턴이란
싱글톤은 오직 1개의 인스턴스를 여러 객체가 공유하여 사용하는 패턴이다. 이 패턴은 자원을 많이 사용하는 객체일 경우 하나의 단일 인스턴스를 만들어 공유하므로서 자원을 아끼기 위해 사용한다.
싱글톤 인스턴스는 단일 스레드를 이용할 때는 구현이 쉬우나 멀티 스레드 환경에서는 어려워질 수 있다.
1. private 생성자를 통한 단일 인스턴스 관리
private 생성자로 인스턴스를 막아놓고 정적 팩토리 메소드를 통해 인스턴스로 접근하는 방법이다.
public class Singleton {
private static final Singleton INSTANCE = new Singleton();
private Singleton() {
// Singleton instance 초기화 작업 수행
}
public static Singleton getInstance() {
return INSTANCE;
}
public void doSomething() {
// Singleton instance를 사용하여 작업 수행
}
}
singleton 을 만드는 가장 간단한 방법이다. 이 방식의 장점은 편리하지만 2가지의 문제점이 있다.
- 직렬화 후 역직렬화를 통한 인스턴스 복제
- 인스턴스를 직렬화 한 후에 역직렬화를 하면 값은 같지만 주소가 다른 새로운 인스턴스가 만들어진다.
- reflection을 이용한 private 생성자 접근
- reflection이란 객체의 정보를 런타임에서 분석하는 방법이다. 접근제한자에 제약 없이 내부에 대한 접근을 하고 메소드를 임의로 호출하거나 변수값을 임의로 바꿀 수 있다.
- private 메소드로 막아놓아도 reflection을 이용해 private 생성자를 접근하여 새로운 생성자를 만들 수 있다.
1번의 단점을 해결하는 방법은 쉽다. 바로 readResolve
메소드를 오버라이딩하는 것이다. 이 함수는 직렬화된 데이터를 역직렬화 하여 객체를 만들 때 호출되는 메소드이다. 이 메소드를 오버라이딩하여 역직렬화를 하여도 새로운 객체가 아닌 기존에 만들어놓은 인스턴스를 반환하도록 작성한다.
import java.io.Serializable;
public class Singleton implements Serializable {
private static final Singleton INSTANCE = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return INSTANCE;
}
private Object readResolve() {
// 항상 Singleton 객체의 인스턴스를 반환
return INSTANCE;
}
}
그러나 이 방법을 사용해도 2번의 단점을 해결할 수 없다.
2. enum를 이용한 싱글톤 인스턴스 관리
enum type를 통해 싱글톤 인스턴스를 만드는 것이다. enum type은 다음의 특징을 가지고 있다.
- reflection을 통한 접근이 불가능하다.
- 직렬화, 역직렬화 코드가 내장되어 있다.
- JVM이 컴파일 타임에서 만들어준다.
즉 enum type를 이용시 private 생성자를 이용한 단점들이 모두 해결이 된다. 아래의 코드는 enum type를 이용한
public enum SingletonEnum {
INSTANCE;
private static class SingletonInstance {
private static final SingletonEnum INSTANCE = new SingletonEnum();
}
public static SingletonEnum getInstance() {
return SingletonInstance.INSTANCE;
}
public void doSomething() {
// Singleton 객체의 동작
}
}
이 방식의 문제점은 enum type이 컴파일 타임에서 생성이 되는 문제가 있다. singleton 인스턴스가 java 프로그램이 실행되는 직후부터 메모리에 상주하기 때문에 공간 낭비가 발생할 수 있다. 즉 메모리에 사용하지도 않는 singleton 인스턴스가 계속 상주하여 공간을 낭비한다. private 생성자를 이용한 방법은 생성자가 호출되는 시점에서 새로 인스턴스를 만들도록 제어할 수 있다. 즉 항상 메모리에 상주시킬 필요가 없다.
결론
본 내용은 singleton 의 인스턴스가 1개만 유지하도록 하는 java만의 기법을 다룬 내용이다. 그렇다 하더라도 본질은 싱글톤이다. private으로 생성하든 enum으로 생성하든 이 방법의 장단점이 무엇이 있으며 싱글톤이 만들어질 컴퓨터의 자원환경에 따라 어떻게 구현할 것인지를 고민해봐야 한다.
Reference
'study > effective java' 카테고리의 다른 글
[item6] 불필요한 객체 생성을 피해라 (0) | 2023.04.25 |
---|---|
[item5] 자원을 직접 명시하기 보단 의존 객체 주입을 사용하라. (0) | 2023.04.25 |
[item4] 인스턴스화를 막기 위해서는 private 생성자를 사용해라. (0) | 2023.04.25 |
[item2] 생성자에 매개변수가 많다면 빌더 패턴을 고려해라 (0) | 2023.04.24 |
[item1] 생성자 대신 정적 팩토리 메소드를 고려해라 (0) | 2023.04.24 |