싱글턴은 응용 프로그램에 의해 생성된 객체를 제한하는 가장 널리 사용되는 생성 디자인 패턴 중 하나입니다. 멀티 스레드 환경에서 사용하는 경우 싱글턴 클래스의 스레드 안전성은 매우 중요합니다. 실제 응용 프로그램에서는 데이터베이스 연결 또는 기업 정보 시스템 (EIS)과 같은 리소스가 제한되어 있으며 리소스 부족을 피하기 위해 현명하게 사용해야합니다. 이를 위해 싱글턴 디자인 패턴을 구현할 수 있습니다. 리소스를 위한 래퍼 클래스를 만들고 런타임에서 생성되는 객체 수를 하나로 제한할 수 있습니다.
자바에서의 스레드 안전한 싱글턴
일반적으로 싱글턴 클래스를 만들기 위해 다음 단계를 따릅니다:
- 새 연산자를 사용하여 새 객체 생성을 방지하기 위해 비공개 생성자를 만듭니다.
- 같은 클래스의 비공개 정적 인스턴스를 선언합니다.
- 공개 정적 메서드를 제공하여 싱글톤 클래스 인스턴스 변수를 반환합니다. 변수가 초기화되지 않은 경우에는 초기화하고, 그렇지 않으면 인스턴스 변수를 그대로 반환합니다.
위 단계를 사용하여 다음과 같은 싱글톤 클래스를 만들었습니다. ASingleton.java
package com.journaldev.designpatterns;
public class ASingleton {
private static ASingleton instance = null;
private ASingleton() {
}
public static ASingleton getInstance() {
if (instance == null) {
instance = new ASingleton();
}
return instance;
}
}
위 코드에서 getInstance() 메서드는 스레드 안전하지 않습니다. 여러 스레드가 동시에 액세스할 수 있습니다. 처음 몇 개의 스레드에서는 인스턴스 변수가 초기화되지 않았으므로 여러 스레드가 if 루프에 진입하여 여러 인스턴스를 생성할 수 있습니다. 이는 싱글톤 구현을 깨뜨릴 것입니다.
싱글톤 클래스에서 스레드 안전성을 어떻게 달성할 수 있을까요?
스레드 안전성을 달성하는 세 가지 방법이 있습니다.
- 클래스 로딩 시점에 인스턴스 변수를 생성합니다.
장점:
- 동기화 없이 스레드 안전성
- 구현이 쉽습니다
단점:
- 응용 프로그램에서 사용되지 않을 수 있는 초기 생성 리소스
- 클라이언트 응용 프로그램이 인수를 전달할 수 없으므로 재사용할 수 없습니다. 예를 들어, 클라이언트 응용 프로그램이 데이터베이스 서버 속성을 제공하는 일반적인 싱글톤 클래스를 가지는 경우입니다.
- getInstance() 메서드를 동기화합니다.
장점:
- 쓰레드 안전성이 보장됩니다.
- 클라이언트 응용 프로그램은 매개변수를 전달할 수 있습니다.
- 게으른 초기화가 달성되었습니다
단점:
- 락 오버헤드로 인한 성능 저하가 있습니다.
- 인스턴스 변수가 초기화된 후에는 필요하지 않은 동기화가 있습니다.
- if 루프 안에 동기화된 블록을 사용하고 변동 변수를 사용합니다
장점:
- 쓰레드 안전성이 보장됩니다
- 클라이언트 응용 프로그램은 매개변수를 전달할 수 있습니다
- 게으른 초기화가 달성되었습니다
- 변수가 널인 경우 처음 몇 개의 스레드에만 적용되는 최소한의 동기화 오버헤드가 있습니다.
단점:
- 추가 if 조건문
쓰레드 안전성을 달성하기 위한 세 가지 방법을 모두 살펴본 결과, 세 번째 방법이 가장 좋은 옵션으로 생각됩니다. 이 경우 수정된 클래스는 다음과 같이 보입니다:
package com.journaldev.designpatterns;
public class ASingleton {
private static volatile ASingleton instance;
private static Object mutex = new Object();
private ASingleton() {
}
public static ASingleton getInstance() {
ASingleton result = instance;
if (result == null) {
synchronized (mutex) {
result = instance;
if (result == null)
instance = result = new ASingleton();
}
}
return result;
}
}
로컬 변수 result
는 불필요해 보일 수 있습니다. 그러나 이것은 코드의 성능을 향상시키기 위한 것입니다. 인스턴스가 이미 초기화된 경우 (대부분의 경우), 변동 필드는 “return instance;” 대신 “return result;” 때문에 한 번만 액세스됩니다. 이는 메서드 전체적인 성능을 최대 25% 향상시킬 수 있습니다. 이를 달성하는 더 나은 방법이 있다고 생각하거나 위의 구현에서 쓰레드 안전성이 저해된 경우 의견을 남겨 주시기 바랍니다.
보너스 팁
문자열은 synchronized 키워드와 함께 사용하기에는 좋은 후보가 아닙니다. 그 이유는 그들이 문자열 풀에 저장되어 있고 다른 코드 조각에서 사용 중인 문자열을 잠그지 않으려고하기 때문입니다. 그래서 저는 객체 변수를 사용하고 있습니다. 자바에서 동기화 및 스레드 안전성에 대해 더 알아보세요.
더 많은 자바 예제를 저희 GitHub 저장소에서 확인할 수 있습니다.
Source:
https://www.digitalocean.com/community/tutorials/thread-safety-in-java-singleton-classes