2023. 2. 12. 17:23ㆍ카테고리 없음
싱글턴 패턴은?
인스턴스가 하나만 만들어져야 하고 이 인스턴스로 글로벌하게 접근 할 수 있는 방법이다.
싱글턴 패턴을 사용하는 이유
메모리 낭비:
인스턴스를 객체 new생성시 마다 만들게 된다면 많은 메모리가 소모된다 , 그렇기에 객체를 static하게 한개만
생성해서 스레드들이 사용할수있게 한다.
-> 싱글톤 패턴은 스프링에서 빈을 싱글톤 패턴을 이용해서 관리합니다.
빈은 스프링 컨테이너에 의해서 생성이 되고 관리되는데 이때 스프링 컨테이너 ( 레지스토리) 가 싱글턴으로 관리합니다.
빈에 등록된 인스턴스는 다른 곳에서 호출을 하여도 객체가 여러개 생성이 되는 것이 아니라 하나의 객체만을 가지고
접근을 합니다.
궁금증?
그렇다면 커넥션 한개를 가지고 다른 클래스에서 호출을 하는가 생각 할수 있지만 그게 아니라.
커넥션을 만들수 있는 객체는 하나만 생성이 되고 그 객체를 static하게 메모리 전역변수에 올려서 그 객체를 통해서
다양한 스레드들이 접근하여 커넥션을 생성 할 수가 있습니다.
근데 여기서 발생하는 문제는 전역변수사용시 개방 폐쇄 원칙에 위배 될 수있습니다.
그러므로 해당 인스턴스는 쓰기 전용이 아니라 읽기 전용이여야 누군가 상태값을 변경하여 다른 곳에 영향을
미치는 것을 막아야 합니다.
이럴때는 로컬변수로 사용하는 방법도 있습니다.
public class Book {
private String name;
private int price;
public Book(){}
}
// BookAppConfig.class
@Configuration
public class BookAppConfig {
@Bean
public Book getBook(){
return new Book();
}
}
@Bean으로 등록
// BookAppConfigTest.class
ApplicationContext ac = new AnnotationConfigApplicationContext(BookAppConfig.class);
Book beanA = ac.getBean(Book.class);
Book beanB = ac.getBean(Book.class);
// 결과 확인
System.out.println("beanA is " + beanA);
System.out.println("beanB is " + beanB);
beanA is hello.core.blog.Book@8692d67
beanB is hello.core.blog.Book@8692d67
beanA와beanB는 같은 객체임을 알수있다.
이는 스프링이 빈을 등록해서 싱글턴으로 관리해주기 때문에 가능하다.
싱글톤 패턴이 안티 패턴이다???
이번에 공부하면서 싱글톤 패턴에 단점을 파헤쳐 보게 되었다.
우선 여기서 말하는 싱글톤 패턴은 디자인 패턴 gof 를 말하는 것이다.
1. 싱글톤 구현 방식
//이른 초기화 방식
public class Person{
private static Person instance = new Person();//이른 초기화
private Person(){ }; //생성자
public static Person getInstance(){
return instance;
}
-> 생성자를 private로 지정하여 다른곳에서 객쳋 생성하지 못하게 함
}
Thread-safe 하게 싱글톤 객체를 생성할수 있다. 단점은 객체의 사용여부와 상관없이 클래스 로딩 시점에 인스턴스가
생성되어 프로그램이 종료될때 까지 메모리를 점유한다.
//늦은 초기화 방식
public class Person{
private static Person instance = null;
private Person(){ }; //생성자
public static Person getInstance(){
if(Instance == null){
Instance = new Person(); //늦은초기화
}
return Instance;
}
-> 생성자를 private로 지정하여 다른곳에서 객체 생성하지 못하게 함
}
초기화를 메소드 호출과 동싱에 하기에 미리 객체생성을 하지 않아서 멀티스레드 환경에서 동시에 호출되어 싱글톤 원칙을 보장 할수 없다.
//쓰레드 안전 초기화 방식
public class Person{
private static Person instance = null;
private Person(){ }; //생성자
public static synchronized Person getInstance(){//동기화를 걸어 한개 쓰레드만 들어갈 수 있게 한다.
if(Instance == null){
Instance = new Person(); //늦은초기화
}
return Instance;
}
-> 생성자를 private로 지정하여 다른곳에서 객체 생성하지 못하게 함
}
Thread-safe 환경이지만 동기화 설정으로 메소드 호출이 많으면 성능이 느려진다.
//DCL(Double-Checked locking) 방식
public class Person{
private volatile static Person instance = null;// cpu 캐시에 저장 말고 바로 메모리에 저장
private Person(){ }; //생성자
public static Person getInstance(){
if(Instance == null){
synchronized(Person.class){
if(Instance == null){
Instance = new Person();
}
}
}
return Instance;
}
-> 생성자를 private로 지정하여 다른곳에서 객체 생성하지 못하게 함
}
위에 코드와 같이 인스턴스가 없을때 synchronized 키워드를 통해 블락을 만들어 하나의 쓰레드만 작업할 수 있도록 하고 블락 내부에서 다시한번 인스턴스 존재 여부를 체크하게 됩니다. 2번 체크하기때문에 DCL(Double Checking Locking) 방식이라 불립니다.
private 접근자로 설정한 INSTANCE 클래스를 보면 volatile이라는 키워드를 사용했습니다. 하나의 프로세서(CPU)는 최적화를 위해 변수를 메인 메모리에 바로 저장하지 않고 캐쉬메모리에 저장하여 성능을 향상시킵니다. 그러나 volatile로 선언된 변수는 변수를 캐쉬메모리에 저장하지 않고 곧바로 메인메모리에 반영하도록 합니다.
volatile 키워드를 사용하지 않고 멀티코어 환경에서 작업한다면 인스턴스를 캐쉬메모리에 저장하여 메인메모리에 인스턴스를 생성하기전에 다른 코어의 쓰레드가 비어있는 메모리를 참조하기때문에 문제가 발생할 수 있습니다.
2. 문제점
- private 생성자를 갖고 있어서 상속이 불가능하다.
- 테스트 하기가 어렵다
- 서버환경에서 싱글톤이 1개만 생성됨을 보장하지 못한다
- 전역 상태를 만들 수 있기 때문에 바람직 하지 못하다.
1. private 생성자를 갖고 있어서 상속이 불가능하다.
오직 싱글톤 클래스 자신만이 자기 오브젝트를 만들도록 제한을 한다.
private 생성자를 가진 클래스는 다른 생성자가 없다면 상속이 불가능하다는 점이고 이것은 객체 지향의 장점인
상속과 이를 이용한 다형성을 적용할수 없다.
2.싱글톤은 테스트 하기가 어렵다.
싱글톤은 초기화 과정에서 생성자 등을 통해 사용할 오브젝트를 다이내믹하게 주입하기도 힘들기 때문에 필요한 오브젝트는 직접 오브젝트를 만들어 사용할 수 밖에 없다.
init을 private로 설정해 생성에 제한이 있다, 그렇다보니 테스트용으로 mock객체로 대체 하기 힘들다.
3.서버환경에서 싱글톤이 1개만 생성됨을 보장하지 못한다
서버에서 클래스 로더를 어떻게 구성하고 있느냐에 따라서 싱글톤 임에도 하나 이상의 오브젝트가 만들어 질수있다.
따라서 자바 언어를 이용한 싱글톤 패턴기법은 서버환경에서는 싱글톤이 꼭 보장 된다고 볼수 없다.
4.전역상태
아무 객체나 자유롭게 접근하고 수정 하고 공유할수 있는 전역 상태를 갖는 다는 것은 객체 지향 프로그래밍 에서는 권장하지 않는 프로그래밍 모델이다.
3.디자인 패턴 싱글톤 과 스프링빈의 싱글톤 차이???
- private 생성자가 필요 없어 상속이 가능하다.
- 테스트하기 편리하다.
- 프레임워크를 통해 1개의 객체 생성을 보장받을 수 있다.
- 객체지향적으로 개발할 수 있다
스프링은 싱글톤 형태의 오브젝트를 만들고 관리하는 기능을 제공하는데 그것이 싱글톤 레지스토리 이다.
스프링 컨테이너는 싱글톤을 생성하고 관리하고 공급하는 컨테이너기도 하다.
여기서 static메소드나,, private 생성자 없이도 싱글톤을 보장하기에 객체 지향적 개발을 할수 있고
테스트 하기도 편리하다.
멀티쓰레드 환경에서 무상태(Stateless) 방식으로 만들어져야 한다
만약 여러 쓰레드들이 동시에 상태를 접근하여 수정한다면 상당히 위험하기 때문이다.
직접 싱글톤을 구현한다면 상당히 많은 단점들이 있겠지만, Spring 프레임워크에서 직접 싱글톤으로 객체를 관리해주므로, 우리는 더욱 객체지향적인 개발을 할 수 있게 된 것이다.
싱글톤 패턴은 직접 만들어서 사용하지 말자, 내가 만들고 있다면 지금 잘못하고 있는 것이다.
- 자바 싱글톤은 클래스로더에 의해 구현되고, 스프링의 싱글 톤은 스프링 컨테이너에 의해 구현된다.
- 자바 싱글톤의 scope는 코드 전체이고, 스프링 싱글톤의 scope는 해당 컨테이너 내부이다.
- 스프링에 의해 구현되는 싱글톤패턴은 Thread safety를 자동으로 보장한다. 자바로 구현하는 싱글톤패턴은 개발자의 로직에 따라 thread safety를 보장할수도, 보장하지 않을수도 있다.
https://seunghyunson.tistory.com/28
스프링 빈의 싱글톤 관리 — 깊게 자라기 (tistory.com)
(22) [GoF 디자인 패턴] 싱글톤 패턴 2부, 멀티쓰레드 환경에서 안전하게 구현하는 방법. - YouTube