초기화
Eager 초기화
클래스 적재시 1회 실행, 클래스 인스턴스 생성을 실제로 사용되기전에 먼저 진행
Static initializer
private static final Singleton INSTANCE = new Singleton();
예제 코드
public class Singleton
{
//1. 정적초기화문 + 2. private 생성자 -> 유일성 보장
private static Singleton uniqueInstance = new Singleton();
private Singleton() {} // 외부 호출 (new Singleton() 불가)
// 3. 모든 client 가 사용할 global access point 제공
public static Singleton getInstance() {
return uniqueInstance;
}
}
Q. 정적초기화 문(assignment statement)에서는 예외처리 불가능
Singleton 객체 생성시 exception 발생 가능성이 있으면 어떻게 할까
Static initializer block
- 프로그램 실행 중 singleton object 가 반드시 필요한 경우 적절한 방법
예제 코드
public class Singleton
{
private static Singleton uniqueInstance;
// other useful instance variables here
private Singleton() {
System.out.println("초기화");
}
static{
try{
if (uniqueInstance == null) {
System.out.println("Creating unique instance of Chocolate Boiler");
uniqueInstance = new Singleton();
}
System.out.println("Returning instance of Chocolate Boiler");
}catch (Exception e){
throw new RuntimeException(e);
}
}
public static Singleton getInstance() {
System.out.println("메소드 호출");
return uniqueInstance;
}
// other useful methods here
}
<결과>
Creating unique instance of Chocolate Boiler
초기화
Returning instance of Chocolate Boiler
메소드 호출
Lazy 초기화
최초로 필요할 때, 클래스 인스턴스의 생성을 처음 실제로 사용될 때 진행
Singleton.getInstance()가 실제로 호출될 때 실행
Multi Thread 환경일 때 if(unqueInstance== null)의 조건문에 대해서 여러 thread가 동시에 들어올 가능성이 존재하기 때문에 위험하다.
이런 경우를 대비하여 thread safe 하게 singleton을 만들어야할 필요가 생겼다.
thread safe singleton
예제 코드
public class Singleton
{
private static Singleton uniqueInstance;
// other useful instance variables here
private Singleton() {}
public static synchronized Singleton getInstance() {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
// other useful methods here
}
• synchronized method는 고비용 연산 + 이미 생성된 경우에도 실행되어야 함.
• 성능이 문제되지 않는 경우 적절(?) 함. (cf) Lazy instantiation using Static Class
Traditional Lazy instantiation using Volatile variable
Volatile variable : JVM 이 최초실행 보장, 자바 코드의 변수를 ‘메인메모리에 저장’ 할 것을 명시하기 위해 쓰인다.
DCL (Double-Checking Locking
예제 코드
public final class Singleton
{
private volatile static Singleton instance = null;
private Singleton() { ... }
public static Singleton getInstance() {
if (singleObject != null) { // 1st CHECK: locking 前 생성 여부 검사 ⇨ 성능 향상.
return singleObject;
} else {
synchronized (Singletom.class) { // lock 획득 時까지 대기.
// 2nd CHECK: 대기 중 다른 스레드가 먼저 lock을 획득 & 객체 생성할 수 있으므로.
if (singleObject == null)
singleObject = new Spooler();
}
return singleObject;
}
}
}
Bill Pugh(Static Holder) singleton
synchronized는 결국 동시성에 제약을 걸어 thread safe를 만족시키는 것입니다. 하지만
synchronized 명령어를 이용하지 않고 Singleton을 thread safe하게 초기화하는 방법이 있습니다. 그게 바로 Bill Pugh Singleton이며 다르게는 static holder singleton 패턴이라고도 부릅니다.
블로그 인용 : https://sabarada.tistory.com/128
// (중첩)클래스는 최초 참조 時 적재되며, 이때 정적초기화文이 실행됨.
private static class Holder { //getInstance() 호출시 실행
public static final Singleton INSTANCE = new Singleton();
// 예외 발생할 수 있으면 정적 초기화 블록 사용
}
예제 코드
public class Singleton
{
private static Singleton uniqueInstance;
private static class Holder{
public static final Singleton uniqueInstance = new Singleton();
}
private Singleton(){}
public static Singleton getInstance()
{
if(uniqueInstance == null){
System.out.println("Createing unique instance of Chocolate Boiler");
uniqueInstance = Holder.uniqueInstance;
}
System.out.println("Returning instance of Chocolate Boiler");
return Holder.uniqueInstance;
}
}
Singleton 클래스 안에 Holder라는 이름의 클래스를 작성하였다.
Holder 클래스는 static을 붙여 메모리에 미리 할당했는데
내부의 변수 singleton도 final을 붙여 차후에 값이 변하지 않도록 상수화 했다.
첫 번째 스레드가 getInstance() 메서드를 호출하면 JVM은 Holder 클래스를 로드(load)하게 되는데,
이때 이미 메모리는 올라가 있으니, JVM 은 이것을 한 번만 로드한다.
이때 중요한 것은 두 번째 thread가 getInstance() 메소드를 호출하더라도,
JVM은 두 번 로드하지 않고 첫 번째 로드가 끝나고 초기화가 완료될 때까지 기다리게 된다.
JVM이 제공하는 동기화 기법에 따른 것이다.
public class MultiThread extends Thread
{
private String name;
public MultiThread(String name) {
this.name = name;
}
public void run() {
int count = 0;
for(int i=0; i<5; i++) {
count++;
Singleton singleton = Singleton.getInstance();
System.out.println(name+ "의"+ count+"번째 쓰레드의 singleton 객체 : " + singleton.toString());
try {
Thread.sleep(400);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
public class SingletonClient
{
public static void main(String[] args){
MultiThread AAA = new MultiThread("AAA");
MultiThread BBB = new MultiThread("BBB");
AAA.start();
BBB.start();
}
}
<실행 결과>
Createing unique instance of Chocolate Boiler
Createing unique instance of Chocolate Boiler
Returning instance of Chocolate Boiler
Returning instance of Chocolate Boiler
AAA의1번째 쓰레드의 singleton 객체 : Singleton@5f89ad23
BBB의1번째 쓰레드의 singleton 객체 : Singleton@5f89ad23
Returning instance of Chocolate Boiler
Returning instance of Chocolate Boiler
AAA의2번째 쓰레드의 singleton 객체 : Singleton@5f89ad23
BBB의2번째 쓰레드의 singleton 객체 : Singleton@5f89ad23
Returning instance of Chocolate Boiler
Returning instance of Chocolate Boiler
BBB의3번째 쓰레드의 singleton 객체 : Singleton@5f89ad23
AAA의3번째 쓰레드의 singleton 객체 : Singleton@5f89ad23
Returning instance of Chocolate Boiler
Returning instance of Chocolate Boiler
BBB의4번째 쓰레드의 singleton 객체 : Singleton@5f89ad23
AAA의4번째 쓰레드의 singleton 객체 : Singleton@5f89ad23
Returning instance of Chocolate Boiler
AAA의5번째 쓰레드의 singleton 객체 : Singleton@5f89ad23
Returning instance of Chocolate Boiler
BBB의5번째 쓰레드의 singleton 객체 : Singleton@5f89ad23
블로그 참고 : https://thisisnew-storage.tistory.com/9
Subclass Singleton
예제 코드
public class Singleton
{
protected static Singleton uniqueInstance;
// other useful instance variables here
protected Singleton() {}
public static synchronized Singleton getInstance() {
if (uniqueInstance == null) {
uniqueInstance = new Singleton();
}
return uniqueInstance;
}
// other useful methods here
}
public class CoolerSingleton extends Singleton
{
// useful instance variables here
protected static Singleton uniqueInstance;
private CoolerSingleton() {
super();
}
// useful methods here
}
public class HotterSingleton extends Singleton
{
// useful instance variables here
private HotterSingleton() {
super();
}
// useful methods here
}
public class SingletonTestDrive
{
public static void main(String[] args) {
Singleton foo = CoolerSingleton.getInstance();
Singleton bar = HotterSingleton.getInstance();
System.out.println(foo);
System.out.println(bar);
}
}
foo, bar 객체를 생성하여 클래스확인결과
같은 싱글톤 객체를 참조하고 있다.
'소프트웨어공학 > 디자인 패턴' 카테고리의 다른 글
Facade 패턴 (퍼사드 패턴) - 구조, 통합 인터페이스 제공 | Design Pattern 디자인 패턴 / (0) | 2021.10.12 |
---|---|
Adapter 어댑터 패턴 - 구조, client 가 요구하는 인터페이스와 제공된 클래스의 인터페이스가 일치하지 않을 때 | Design Pattern 디자인 패턴 (0) | 2021.10.10 |
Singleton 패턴 클래스의 인스턴스가 하나만 있는 것 | Design Pattern 디자인 패턴 (0) | 2021.10.05 |
[Design Pattern] 디자인 패턴 / 설계 원칙 .java (0) | 2021.10.02 |
[Design Pattern] 디자인 패턴 / UML Base .java (0) | 2021.09.11 |
댓글