public class Singleton {
private static Singleton instance;
private Singleton() {
}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
싱글톤 인스턴스 생성 여부를 확인하여 없다면 새로 생성하고 있다면 만들어진 인스턴스를 반환합니다.
멀티스레드 환경에서 2개 이상의 인스턴스가 만들어질 수 있습니다. => 원자성이 결여되어있습니다.

싱글톤 인스턴스가 생성되지 않은 상태에서 2개의 쓰레드가 위와 같은 순서로 싱글톤 인스턴스에 접근하게 된다면 싱글톤 인스턴스가 2번 초기화가 될 수 있습니다.
synchronized 키워드를 사용하여 생성public class Singleton {
private static Singleton instance;
private Singleton() {
}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
먼저 접근한 쓰레드에게 getInstance() 메서드가 인스턴스를 반환할 때까지 다른 쓰레드가 접근할 수 없도록 lock을 걸 수 있습니다.
getInstance() 메서드를 호출할 때마다 lock이 걸려서 성능저하가 될 수 있습니다.
synchronize키워드와 성능
synchronize키워드를 사용하면 자바 내부적으로 메서드나 변수를 처리하기 위해 block과 unblock을 처리합니다. 이 과정을 실행할 때 쓰레드는 많은 비용이 드는 인스트럭션 재배열 작업이 필요하기 때문에synchronize메서드가 많이 호출되면 프로그램 성능이 저하될 수 있습니다.
static) 멤버public class Singleton {
private final static Singleton instance = new Singleton();
private Singleton() {
}
public static Singleton getInstance() {
return instance;
}
}
static 멤버로 설정해둔 싱글톤 인스턴스를 생성해둡니다.getInstance() 메서드는 이미 만들어진 인스턴스를 반환만 해줍니다.싱글톤 인스턴스가 필요하지 않은 경우에도 항상 인스턴스가 만들어지기 때문에 불필요한 자원낭비라는 문제점이 있습니다.
static) 블록public class Singleton {
private static Singleton instance = null;
static {
instance = new Singleton();
}
private Singleton() {
}
public static Singleton getInstance() {
return instance;
}
}
특징과 문제점은 3번 정적(static) 멤버와 같습니다.
static) 멤버와 Lazy Holder(중첩 클래스)class Singleton {
private static class singleInstanceHolder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return singleInstanceHolder.INSTANCE;
}
}
singleInstanceHolder의 인스턴스는 Singleton 클래스 안에서 생성되지 않으므로 singleInstanceHolder는 Singleton 클래스 로딩 시점에 로딩되지 않습니다.getInstance()의 메서드가 호출될 때까지 싱글톤 인스턴스의 생성을 미룰 수 있습니다.thread-safe가 보장되기 때문에 synchronize 키워드 없이도 멀티쓰레드 환경에서도 안전합니다.public class Singleton {
private volatile Singleton instance;
private Singleton() {
}
public Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
synchronize 키워드로 잠금 전에 확인synchronize 키워드로 잠금 후 인스턴스를 생성하기 전에 확인synchronize 키워드만을 사용했을 때의 성능저하 문제점을 해결할 수 있습니다.
volatile키워드
멀티쓰레드 환경에서 각각의 쓰레드는 변수를 메인메모리가 아닌 캐시메모리에서 가져오게 됩니다. 따라서 변수 공유가 되지 않는다는 문제점이 발생할 수 있습니다. 하지만volatile키워드를 쓰게 되면 변수를 메인 메모리를 기반으로 저장하고 읽어오게 만들기 때문에 위와 같은 문제가 생기지 않습니다.
enumpublic enum SingletonEnum {
INSTANCE;
public void singletonMethod() {
}
}
enum은 자바 언어 적으로 인스턴스가 1개임이 보장됩니다.enum 인스턴스는 기본적으로 thread-safe가 보장되기 때문에 싱글톤 인스턴스로 활용될 수 있습니다.enum은 기본적으로 serializable가 가능합니다. 따라서 readObject() 와 같은 Serializable interface를 구현할 필요가 없으므로 역직렬화시 새로운 객체가 생성되지 않음을 보장합니다.enum의 생성자는 접근제한자는 기본적으로 private 이라 Reflection을 통한 생성자 접근이 원천적으로 차단됩니다. 따라서 Reflection으로 외부에서 enum class를 인스턴스화 할 수 없습니다.Effective Java 의 저자 조쥬아 블로크에 따르면 싱글톤을 구현하기 가장 좋은 방법은
enum을 사용하는 방법이라고 합니다.