지난 시간 Instance 수를 제한하는 Singleton 패턴을 배웠었고, 과제로 한 개의 Instance 만 제한하는 것이 아닌 특정 개수만큼의 Instance 를 제한하는 Multiton 을 만들어 보도록 하였습니다.


관련 내용은 아래 포스팅을 참고!



똑똑한 우리 스터디 구성원 모두 과제를 잘해왔고, 정리하는 차원에서 제가 만든 Multiton 을 Review 해보도록 하겠습니다.


일단 가정은 요구사항의 SetInstanceCount 시점에 Instance pool 을 만드는 것이 아닌(Eager-binding), 개수만 먼저 제한하고 필요시점마다 Instance 를 생성하는 게으른 할당(Lazy-binding)으로 제작해 보겠습니다.


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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
public class Multiton {
    // INSTANCE POOL 관리.
    // 어떤 세팅도 하지 않았으면, Singleton 으로 관리하도록 처리.
    private static int INSTANCE_LIMIT_COUNT = 1;
 
    // Instance pool.
    private static HashMap<Integer, Multiton> POOL = new HashMap<>();
 
    private static Integer toggleCount = 0;
 
    private String toStringMessage;
 
    /**
     * 외부에서 Instance 를 만들지 못하도록 접근제한.
     */
    private Multiton(){
        // Instance 의 생성과 함께 POOL 에 넣는다.
        Integer number = POOL.size();
 
        toStringMessage = String.format("%d번 인스턴스", number);
        POOL.put(number, this);
    }
 
    /**
     * Instance 출력.
     *
     * <pre>
     *     Lock 을 메소드에 모두 걸어 Thread-safe 를 보장.
     * </pre>
     *
     * @return
     */
    public static synchronized Multiton GetInstance() {
        final Multiton result;
 
        if (POOL.size() < INSTANCE_LIMIT_COUNT) {
            // Instance 개수가 아직 제한된 POOL 만큼 생성안된 경우.
            result = new Multiton();
        } else {
            // Instance 개수가 아직 제한된 POOL 만큼 생성 된 경우.
            result = POOL.get(toggleCount);
            toggleCount = (toggleCount + 1) % INSTANCE_LIMIT_COUNT;
        }
 
        return result;
    }
 
    /**
     * 인스턴스의 개수 제한
     *
     * @param limitCount
     */
    public static final void SetInstanceCount(int limitCount) {
        if (limitCount < 0) {
            throw new RuntimeException("[에러] Instance 개수는 0보다 커야 합니다.");
        }
        INSTANCE_LIMIT_COUNT = limitCount;
    }
 
    @Override
    public String toString(){
        return toStringMessage;
    }
}
cs


코드 내부의 구체적인 주석을 명시 하였고, 주의깊게 봐야할 부분은 아래와 같습니다.


1. Thread-safe 보장을 위해 Instance 를 유일하게 외부로 내보낼 수 있는 GetInstance 메소드에 synchronized 키워드를 달았습니다.


2. 사용자가 SetInstanceCount 를 사용하지 않을 수 있기 때문에 Instance 개수 제한을 명시적으로 1로 초기화하였습니다.

(INSTANCE_LIMIT_COUNT = 1)


이렇게 제작된 코드를 Thread-safe 가 보장이 되는지 확인해보도록 하겠습니다.


아래 테스트는 Thread 를 50 개 제작하여, 실행하는 메소드입니다. 각 스레드에서는 각 Instance 의 toStringMessage 를 출력하도록 합니다.


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
39
40
41
42
43
44
// Thread Test using JAVA7
{
    System.out.println("멀티톤 테스트!!");
    Multiton.SetInstanceCount(5);
 
    // Thread 목록 생성.
    final ArrayList<Thread> multiThreadTestList = new ArrayList<>();
    {
         for (int i = 0; i < 50++i) {                    
            Thread newThread = new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Multiton.GetInstance());
                }
            });
 
            multiThreadTestList.add(newThread);
        }
    }
 
    // Thread 실행
    {
        // Thread 생성 즉시 바로 실행하지 않고, 목록을 생성하고 실행하는 이유는 최대한 동시에 Thread 를 실행하고 싶기 때문!
        // Thread Instance 를 만드는 시간 때문에 제대로 테스트가 안될 가능성 존재.
        for (Thread thread : multiThreadTestList) {
            thread.start();
        }
    }
}
 
// Thread Test using JAVA8
{
    IntStream.rangeClosed(050).mapToObj(n -> new Thread(() -> System.out.println(Multiton.GetInstance()))).forEach(Thread::start);
}
 
// CONSOLE LOG
// 멀티톤 테스트!!
// 0번 인스턴스
// 1번 인스턴스
// 2번 인스턴스
// 3번 인스턴스
// 4번 인스턴스
// 1번 인스턴스
// 3번 인스턴스
cs


CONSOLE LOG 를 보면, Instance 가 꼭 순서대로 나오지는 않는 것을 볼 수 있습니다. 

비록 GetInstance 에서 LOCK 처리는 하였지만 Thread 의 실행 상태에 따라 순서가 바뀔 수는 있습니다. 

순서를 보장해야만 한다면 Blocking 처리 등 더 복잡한 과정이 필요하겠지만, 이번 주제는 Instance 의 개수를 제한하는 것이니 다루지 않을 예정입니다.


이 블로그가 스터디를 참여하고 있는 모두에게 좋은 정보가 되길 바랍니다. :-)

반응형
Posted by N'