파이문

멀티쓰레드에서 싱글톤 클래스 사용 예제 본문

Java

멀티쓰레드에서 싱글톤 클래스 사용 예제

민Z 2020. 10. 8. 21:03

싱글톤 클래스 예제는 보통 아래 처럼 작성되곤 한다.

  public static class Singleton {
        private static Singleton instance;

        private Singleton() {
        }

        public static Singleton getInstance() {
            if (instance == null) {
                instance = new Singleton();
            }
            return instance;
        }

    }

이렇게 작성하면 단일 쓰레드에서는 문제가 없지만, 멀티 쓰레드(multithread) 에서는 instance 를 가져올 때 (동시 접근하여 instance 를 null 로 판단한 경우) 문제가 생길 수 있다. 

 

가장 쉬운 방법은 getInstance 함수에 synchronized 키워드를 넣는 것이지만, synchronized 는 성능상의 문제가 있다. 그래서 나온 방법이 LazyHolders 이다.

 

아래의 예제는 synchronized 로 된 싱글톤 클래스와 LazyHolder 기법이 적용된 싱글톤 클래스를 멀티쓰레드로 각각 돌리는 것이다.

import java.util.concurrent.CountDownLatch;

public class Singleton {

    public static class SyncTickets {
        private static int ticket;
        private static SyncTickets instance;

        private SyncTickets() {
        }

        public synchronized static SyncTickets getInstance() {
            // 멀티 쓰레드 환경에서 싱글톤 인스턴스를 하나만 만들기 위해 synchronized 키워드를 넣는다.
            // 단 성능상 문제가 있다.
            if (instance == null) {
                System.out.println("Call Only Once");
                instance = new SyncTickets();
            }
            return instance;
        }

        public synchronized int getTicket() {
            return ticket++;
        }
    }

    public static class LazyTickets {
        private static int ticket;

        private LazyTickets() {
        }

        // JVM에게 객체의 초기화를 떠님기는 방식으로, 멀티스레드 환경에서도 객체의 단일성을 보장할 수 있다.
        // Class 를 로딩하고 초기화하는건 JVM 의 영역이고 Thread Safe 를 보장한다.
        public static LazyTickets getInstance() {
            return LazyHolder.INSTANCE;
        }

        private static class LazyHolder {
            private static final LazyTickets INSTANCE = new LazyTickets();
        }

        public synchronized int getTicket() {
            return ticket++;
        }
    }

    public static void main(String[] args) throws InterruptedException {

        // 표준 출력에서 synchronized 적용된 싱글톤 클래스를 먼저 보려고 임의로 넣음
        CountDownLatch countDownLatch = new CountDownLatch(10);

        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                SyncTickets tickets = SyncTickets.getInstance();
                System.out.println(tickets.hashCode());
                System.out.println("Sync-> " + Thread.currentThread().getName() + ":" + tickets.getTicket());
                countDownLatch.countDown();
            }).start();
        }

        countDownLatch.await();
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                LazyTickets tickets = LazyTickets.getInstance();
                System.out.println(tickets.hashCode());
                System.out.println("Lazy-> " + Thread.currentThread().getName() + ":" + tickets.getTicket());
            }).start();
        }
    }
}

'Java' 카테고리의 다른 글

JDK 14 톺아보기  (0) 2020.10.07
Mockito 를 사용하는 예제  (0) 2020.09.25
CountDownLatch vs CyclicBarrier  (0) 2020.09.24
멀티쓰레드 환경에서 캐시 구현하기  (0) 2020.06.27
Comments