자바의 싱글턴 패턴은 하나의 클래스에 대해 단 하나의 인스턴스만 생성되는 것을 보장하는 디자인 패턴이다.
이 패턴은 프로그램 전체에서 동일한 객체를 사용해야할 때 유용하다.
주로 자원을 많이 사용하는 객체나 전역적으로 상태를 관리하는 객체를 만들 때 사용된다.
private static변수를 선언한다.public static메서드를 통해 유일한 인스턴스에 접근할 수 있또록 한다.public class Singleton {
// 유일한 인스턴스를 저장할 정적 변수
private static Singleton instance;
// private 생성자: 외부에서 직접 인스턴스를 생성하지 못하게 막음
private Singleton() {
}
// 전역에서 인스턴스에 접근할 수 있는 메서드
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
Singleton 클래스의 인스턴스가 처음 요청될 때만 생성되고, 이후에는 이미 생성된 인스턴스를 반환한다.
위의 기본 싱글턴 구현은 단일 스레드 환경에서는 문제가 없겠지만, 멀티스레드 환경에서는 여러 스레드가 동시에 getInstance()를 호출하면 여러 개의 인스턴스가 생성될 수 있다.
이를 방지하기 위해 synchronized키워드를 사용해 동기화된 접근을 보장할 수 있다.
synchronized는 한 번에 하나의 스레드만 해당 블록이나 메서드에 접근할 수 있도록 보장한다.
public class Singleton {
private static Singleton instance;
private Singleton() {
}
// synchronized를 사용하여 멀티스레드 환경에서 안전하게 처리
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
성능 저하를 줄이기 위해, 인스턴스가 생성된 후에는 동기화를 피할 수 있는 더블 체크 잠금 방식을 사용할 수 있다.
volatile키워드를 사용해 인스턴스 변수를 메모리에서 직접 읽도록 보장한다.
volatile키워드는 변수의 값을 여러 스레드가 동시에 읽고 쓸 때 발생할 수 있는 가시성 문제를 해결해준다.
즉, 이 키워드를 변수에 붙이면, 그 변수는 스레드 간에 즉시 메모리의 일관성을 유지하도록 강제된다. 수정 시 바로 메인 메모리에 기록되는 것이다. 그리고, 다른 메모리가 해당 변수를 읽을 때는 반드시 메인 메모리에서 최신 값을 가져온다.
자바에서 멀티스레드 프로그램을 작성할 때, 여러 스레드가 동시에 변수에 접근하고 수정하는 경우가 많다. 하지만 자바의 JVM과 CPU는 최적화를 위해 변수의 값을 각 스레드의 캐시에 저장하고, 필요할 때만 메인 메모리와 동기화합니다. 이로인해 한 스레드에서 변수 값을 수정하더라도 다른 스레드에서 이를 인식하지 못하는 상황이 발생할 수 있다.
이를가시성 문제라고 한다!
public class Singleton {
private static volatile Singleton instance;
private Singleton() {
}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
싱글턴 인스턴스를 미리 생성해두는 방식도 있다. 이 방법은 인스턴스가 필요하지 않더라도 클래스가 로드될 때 생성되므로 메모리 낭비가 있을 수 있지만, 멀티 스레드 환경에서는 안전하다.
public class Singleton {
// 클래스 로드 시 바로 인스턴스를 생성
private static final Singleton instance = new Singleton();
private Singleton() {
}
public static Singleton getInstance() {
return instance;
}
}