꾸물꾸물 졔의 개발공부
1. 싱글톤 패턴 (Singleton Pattern) 본문
💡 디자인 패턴이란,
프로그램을 설계할 때 발생했던 문제점들을 객체 간의 상호 관계 등을 이용하여 해결할 수 있도록 하나의 '규약' 형태로 만들어 놓은 것을 의미.
소스나 코드로 바로 전환가능한 완성된 디자인은 아니며, 상황에 맞게 사용될 수 있는 문제 해결을 위한 템플릿.
싱글톤 패턴이란 ?
하나의 클래스에 오직 하나의 인스턴스만 가지는 패턴이다.
즉, 인스턴스가 필요할 때마다 새로운 인스턴스를 만드는 것이 아닌, 기존의 인스턴스를 활용하는 것이다.
생성자가 여러번 호출되어도, 실제로 생성되는 객체는 하나이며 최초로 생성된 이후에 호출된 생성자는 이미 생성된 객체를 반환시키도록 한다.
다음과 같은 상황에 싱글톤 패턴을 주로 사용한다.
- 프로그램 내에서 하나의 객체만 존재함이 보장되어야 한다.
- 프로그램 내의 여러 부분에서 해당 객체를 공유하며 사용해야 한다.
(ex : 데이터베이스에서 커넥션풀, 스레드풀, 캐시, 로그 기록 객체 등)
싱글톤 패턴을 사용하는 이유 (=장점)
하나의 인스턴스만 사용하는 싱글톤 패턴의 이점은 다음과 같다.
1. new 연산자를 사용하여 새로운 객체를 생성할 때마다 메모리 영역을 할당받아야 하는데, 싱글톤 패턴을 사용하게 된다면 한개의 인스턴스만을 고정 메모리 영역에 생성하고 추후 해당 객체를 접근할 때 메모리 낭비를 방지할 수 있다.
2. 초기에 한번 생성된 이후에 인스턴스를 사용할 때에는, 이미 생성된 인스턴스를 활용하기 때문에 속도 측면에 이점이 있다.
3. 싱글톤으로 구현한 인스턴스는 '전역' 이므로, 다른 여러 클래스의 인스턴스들이 데이터를 공유하는 것이 가능하다.
(단, 동시성 문제가 발생할 수 있기 때문에 이 점은 유의해야 한다.)
단점
1. 싱글톤 인스턴스가 혼자 너무 많은 일을 하거나, 많은 데이터를 공유시키면 다른 클래스들간의 결합도가 높아지게 되는데, 결합도가 높아지면 유지보수가 힘들고 테스트가 원활하게 진행될 수 없다.
2. TDD(Test Driven Development)를 할 때 걸림돌이 된다.
TDD를 할 때에는 주로 단위 테스트를 하는데, 단위 테스트는 테스트가 서로 독립적이어야 하며 테스트를 어떤 순서로든 실행할 수 있어야 한다.
하지만 싱글톤 패턴은 미리 생성된 하나의 인스턴스를 기반으로 구현하는 패턴이기 때문에 각 테스트마다 '독립적인' 인스턴스를 만들기가 어렵다.
싱글톤 패턴 구현하기
public class Singleton{
//단 1개만 존재해야 하는 객체의 인스턴스로, static으로 선언
private static Singleton instance;
//""private"" 생성자로 외부에서의 접근을 막아야한다.
private Singleton(){}
//외부에서는 getInstance()로 instance 접근
public static Singleton getInstance() {
//초기에 인스턴스가 한번도 생성되지 않았을 경우,
if(instance == null) {
instance = new Singleton();
}
//이미 생성된 인스턴스가 있을 경우, 바로 반환
return instance;
}
}
싱글톤 패턴의 기본적인 구현 방법은 다음과 같다.
private static으로 Singleton객체의 instance를 선언.
getInstance() 메서드가 '처음' 실행 될 때만 하나의 instance가 생성되고 이후부터는 이미 생성되어진 instance를 return 하는 방식으로 진행된다.
중요한 것은 private으로 된 기본 생성자이다. 생성자를 private로 생성하여 외부에서 새로운 객체를 생성하는 것을 (new Singleton()) 막아줘야 한다.
//같은 instance 인지 주소값으로 테스트
public class Main{
public static void main(String[] args){
SingleTon singleton1 = Singleton.getInstance();
SingleTon singleton2 = Singleton.getInstance();
if(a==b) System.out.println(true);
}
}
/*
Output : true
*/
멀티스레드에서의 싱글톤
Multi-thread 환경에서 싱글톤을 사용하게 되면, 다음과 같은 문제가 발생할 수 있다.
1. 여러개의 인스턴스 생성
Multi-thread 환경에서, 초기 생성되어진 instance가 없을 때 동시에 getInstance() 메소드를 호출하게 되면 각각 새로운 instance를 생성하여 2개 이상의 인스턴스가 만들어 질 수 있다.
2. 변수 값의 일관성 실패
다음과 같은 코드가 실행될 때, 두개 이상의 thread에서 plusCount() 를 동시에 실행하게 되면, 일관되지 않은 값이 발생할 수 있다.
public class Singleton{
private static Singleton instance;
private static int count=0;
private Singleton() {}
public static Singleton getInstance{
if(instance == null){
instance = new Singleton();
}
return instance;
}
public static void plusCount() {
count++;
}
}
💡해결법 (멀티스레드에서의 싱글톤 사용)
1. 정적 변수선언에서 인스턴스 생성
static 변수로 singleton 인스턴스를 생성하는 방법으로 해결할 수 있다. 아래와 같이 초기에 인스턴스를 생성하게 된다면 multi-thread 환경에서도 다른 객체들은 getInstance()를 통해 하나의 인스턴스만을 공유할 수 있다.
public class Singleton{
private static Singleton instance= new Singleton(); //초기에 인스턴스 생성
private Singleton() {}
public static Singleton getInstance() {
return instance;
}
}
2. synchronized 사용
synchronized, 동기화 방식을 사용하여 multi-thread의 동시성 문제를 해결하고 안전하게 인스턴스를 만들 수 있다. 하지만 해당 방법은 Thread-safe를 보장하기 위해 큰 성능저하를 발생시키므로 권장하지 않는 방법이다.
public class Singleton{
private static Singleton instance;
private Singleton() {}
public static synchronized Singleton getInstance() {
if(instance == null){
instance = new Singleton();
}
return instance;
}
}
3. holder에 의한 초기화
클래스 안에 클래스(holder)를 두어 JVM으로 부터 클래스가 로드되는 시점을 이용한 방법이다.
JVM의 클래스 초기화 과정에서 보장되는 원자적 특성을 이용하여 싱글톤의 초기화 문제에 대한 책임을 JVM에게 떠넘기는 것이다.
아래 코드에서 클래스 안에 선언한 holder 클래스에서 선언된 인스턴스는 static 이기 때문에 클래스 로딩 시점에서 한번만 호출된다. 또한 final을 사용해 다시 값이 할당되지 않도록 만드는 방식을 사용한다.
public class Singleton{
private Singleton() {}
private static class Holder{
public static final Singleton instance = new Singleton();
}
public static Singleton getInstance() {
return Holder.instance;
}
}
💡결론
싱글톤 패턴은 메모리, 속도, 데이터 공유 측면에서 확실한 이점을 가지고 있다. 하지만 무조건적으로 좋은 것만은 아니다. 앞서 말했듯이 강한 결합도로 인한 유지보수나 테스트 문제, 그리고 multi-thread 환경에서는 동시성 문제가 발생할 수 있기 때문에 반드시 싱글톤이 필요한 상황이 아니라면 지양하는 것이 좋다고 한다.
싱글톤 패턴을 사용하고자 한다면 "꼭 한개만 존재하여야 하는지?" , "동시성 문제가 발생한 가능성은 없는지?" 를 따지며 신중하게 사용해야한다.
'CS > Design Pattern' 카테고리의 다른 글
[Design Pattern] 디자인 패턴이란 ? (1) | 2022.09.11 |
---|