상세 컨텐츠

본문 제목

[디자인패턴] 싱글턴 패턴

CS구멍/디자인패턴

by :Eundms 2024. 5. 1. 12:00

본문

1. 즉시 초기화

public class IdGnerator {
    private AtomicLong id = new AtomicLong(0);
    private static final IdGenerator instance = new IdGenerator();
    
    private IdGenerator() {}
    public static IdGenerator getInstance() {
    	return instance;
    }
    
    public long getId() {
    	return id.incrementAndGet();
    }
}

 

2. 늦은 초기화

public class IdGnerator {
    private AtomicLong id = new AtomicLong(0);
    private static IdGenerator instance;
    
    private IdGenerator() {}
    
    public static synchronized IdGenerator getInstance() {
    	if(instance == null) {
        	instance = new IdGenerator();
        }
    	return instance;
    }
    
    public long getId() {
    	return id.incrementAndGet();
    }
}

- 시간이 오래 걸리는 초기화 작업이 인스턴스가 사용되기 직전에 이루어지면 성능상 문제 발생 가능

- synchronized 키워드를 사용하는데, IdGenerator 클래스의 사용 빈도가 높다면 자주 잠금이 일어나고 이로 인해 병목 현상이 발생할 수 있음

 

3. 이중 잠금

- 늦은 초기화 방식 + 낮은 동시성을 해결

public class IdGenerator {
    private AtomicLong id = new AtomicLong(0);
    private static IdGenerator instance;
    
    private IdGenerator() {}
	
    public static IdGenerator getInstance() {
    	if(instance == null) {
        	synchronized(IdGenerator.class) {
            	if(instance == null) {
                	instance = new IdGenerator();
                }
            }
        }
        return instance;
    }
    
    public long getId() {
    	return id.incrementAndGet();
    }
}

- CPU 명령이 재정렬되면

IdGenerator 클래스의 객체가 new 예약어를 통해 instance 멤버 변수가 지정딘 후,

초기화가 이루어지기 전에 다른 스레드에서 이 객체를 사용하려고 할 수 있음

- volatile 키워드를 인스턴스 멤버 변수에 추가하여 명령어 재정렬 방지

 

4. 홀더에 의한 초기화

- 지연 로딩 + 스레드 안정성 

- 정적 내부 클래스 활용

public class IdGenerator {
	private AtomicLong id = new AtomicLong(0);
    private IdGenerator() {}
    private static class SingletonHolder {
    	private static final IdGenerator instance = new IdGenerator();
    }
    public static IdGenerator getInstance() {
    	return SingletonHolder.instance;
    }
    public long getId() {
    	return id.incrementAndGet();
    }
}

IdGenerator 적재 시점에는 SingetonHolder는 적재되지 않지만,

getInstance()가 처음으로 호출될때 적재되고 생성된다

따라서 JVM에 의해 인스턴스의 유일성과 생성 프로세스의 스레드 안정성이 보장된다

 

5. 열거

- enum 활용하여 인스턴스 생성시 스레드 안정성과 유일성을 보장함

public enum IdGenerator {
	INSTANCE;
    private AtomicLong id = new AtomicLong(0);
    public long getId() {
    	return id.incrementAndGet();
    }
}

싱글톤 패턴은

클래스 간의 의존성을 감춘다

코드의 확장성/테스트에 영향을 미친다

매개변수가 있는 생성자를 지원하지 않는다

 

대안 ? 팩터리 패턴, DI 컨테이

관련글 더보기

댓글 영역