본문 바로가기
소프트웨어공학/디자인 패턴

싱글톤 패턴 코드 (Singleton pattern java code) | Design Pattern 디자인 패턴 /

by javapp 자바앱 2021. 10. 8.
728x90

초기화

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 객체를 생성하여 클래스확인결과

같은 싱글톤 객체를 참조하고 있다.

 

댓글