Posts [개발자 블로그] 싱글톤 패턴(Singleton Pattern)
Post
Cancel

[개발자 블로그] 싱글톤 패턴(Singleton Pattern)

싱글톤 패턴(Singleton Pattern)

  • Singleton 패턴은 인스턴스를 불필요하게 생성하지 않고 오직 JVM내에서 한 개의 인스턴스만 생성하여 재사용을 위해 사용되는 디자인패턴

프린터 관리자 Example

  • 단 하나의 프린터 객체를 만들어 사용자 객체가 사용하려 한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public class User {
    private String name;
    public User(String name) {
        this.name = name;
    }
    
    public void print() {
        Printer printer = printer.getPrinter();
        printer.print(this.name + " print using " + printer.toString() + ".");
    }
}
public class Printer {
    private static Printer printer = null;
    private Printer() { }
    
    public static Printer getPrinter() {
        if (printer = = null) {
             printer = new Printer(); // Printer 인스턴스 생성
        }
        return printer;
    }

    public void print(String str) {
      System.out.println(str);
	}
}

public class Main {
    private static final int User_NUM = 5;
    
    public static void main(String[] args) {
        User[] user = new User[User_NUM];
        for (int i = 0; i < User_NUM; i++) {
        user[i] = new User((i + 1) + "-user"); // User 인스턴스 생성
        user[i].print();
      }
    }
}
  • 실행 결과
    image

문제점

  • printer 인스턴스가 아직 생성되지 않았을 때 스레드 1이 getPrinter메서드의 if문을 실행해 이미 인스턴스가 생성되었는지 확인한다. 현재 printer 변수는 null인 상태다. 만약 스레드 1이 생성자를 호출해 인스턴스를 만들기 전 스레드 2가 if문을 실행해 printer 변수가 null인지 확인한다. 현재는 null이므로 인스턴스를 생성하는 코드, 즉 생성자를 호출하는 코드를 실행하게 된다. 스레드1도 스레드2와 마찬가지로 인스턴스를 생성하는 코드를 실행하게 되면 결과적으로 Printer클래스의 인스턴스가 2개 생성된다.
    • => 다중 스레드 문제로 인한 동기화 처리가 요구된다.

해결책

  • 1) 정적 변수에 인스턴스를 만들어 바로 초기화하는 방법(비추천)
    • 클래스가 참조 될 때 메모리(JVM)에 로딩되어 생성됨 , instance변수가 굳이 바인딩 될 필요가 없어도 클래스를 참조하는 순간, 불필요하게 객체가 생성됨
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Printer {
	private static Printer instance = new Printer();

	private Printer() {
	}

	public static Printer getPrinter() { // synchronized 임계구역설정
		return instance;
	}

	public void print(String str) {
		System.out.println(str);
	}
}

  • 2) 인스턴스를 만드는 메서드에 동기화하는 방법(비추천)
    • synchronized getinstance()의 경우 인스턴스를 리턴 받을 때마다 Thread동기화 때문에 불필요하게 lock이 걸리게 되어 비용 낭비가 크다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Printer {
	private static Printer instance = null;

	private Printer() {
	}

	public synchronized static Printer getPrinter() { // synchronized 임계구역설정
        if(instance == null) {
            instance = new Printer();
        }
		return instance;
	}

	public void print(String str) {
		System.out.println(str);
	}
}
  • 3) LazyHolder 기법(가장 이상적인 방법)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Printer {

	private Printer() {
	}

	public static class LazyHolder {
		private static Printer instance = new Printer();
	} // getPrinter가 호출되는 순간 이 클래스가 메모리에 적재되어 instance 변수가 생성됨

	public static Printer getPrinter() { // synchronized 임계구역설정
		return LazyHolder.instance;
	}

	public void print(String str) {
		System.out.println(str);
	}

}

출처

This post is licensed under CC BY 4.0 by the author.

[개발자 블로그] 디자인 패턴

[개발자 블로그] 추상 팩토리 메서드 패턴(Abstract Factory Method Pattern)