2022. 8. 11. 17:36ㆍJAVA
이 글은 'baeldung - How Does the Spring Singleton Bean Serve Concurrent Requests?' 를 보고 번역, 학습한 내용을 정리한 글입니다.
1. 개요
싱글톤 스코프로 생성된 스프링 빈이 동시에 들어오는 여러 요청을 처리하기 위해 보이지 않는 곳에서 어떻게 작동하는지 맛볼 것입니다.
또한 Java가 Bean 인스턴스를 메모리에 적재하는 방법과 이에 대한 동시 액세스를 처리하는 방법을 이해해봅시다 ^~^
2. 스프링 빈과 자바 힙메모리
Java 힙 메모리는 애플리케이션 내에서 실행 중인 모든 스레드가 액세스할 수 있는 전역 공유 메모리입니다.
Spring 컨테이너가 싱글톤 스코프의 빈을 생성할 때 빈은 힙에 올라갑니다.
스프링의 방식대로라면, 모든 스레드가 동일한 Bean 인스턴스를 가리킬 수 있습니다.
다음으로 스레드의 스택 메모리가 무엇이며 동시 요청을 처리하는 데 스택이 어떤 도움을 주는지 알아봅시다.
3. 싱글톤 스코프에서 동시 요청은 어떻게 처리될까?
싱글톤 스코프의 ProductService 빈을 봅시다.
@Service
public class ProductService {
private final static List<Product> productRepository = asList(
new Product(1, "Product 1", new Stock(100)),
new Product(2, "Product 2", new Stock(50))
);
public Optional<Product> getProductById(int id) {
Optional<Product> product = productRepository.stream()
.filter(p -> p.getId() == id)
.findFirst();
String productName = product.map(Product::getName)
.orElse(null);
System.out.printf("Thread: %s; bean instance: %s; product id: %s has the name: %s%n", currentThread().getName(), this, id, productName);
return product;
}
}
이 서비스에는 호출자에게 제품 데이터를 반환하는 getProductById() 메서드가 있습니다.
또한 이 Bean에서 생성된 데이터는 URI '/product/{id}'를 가지는 컨트롤러로 반환됩니다.
다음으로, 런타임에서 동시에 이루어진 요청이 엔드포인트 /product/{id}에 도달할 때 어떤 일이 발생하는지 살펴보겠습니다.
특히, 첫 번째 스레드는 /product/1을 호출하고 두 번째 스레드는 /product/2를 호출합니다.
콘솔 출력으로 미루어 보았을 때, Spring은 여러 스레드에서 동일한 Bean 인스턴스를 사용할 수 있습니다.
각 스레드에 대해 Java가 각각 스택 메모리를 생성하기 때문입니다.
스택 메모리는 스레드 실행 중에 메서드 내부에서 사용되는 지역 변수의 상태를 저장하는 역할을 합니다.
이런 식으로 Java는 병렬로 실행되는 스레드가 서로의 변수를 덮어쓰지 않도록 합니다.
두 번째로, 'ProductService' 빈은 힙 수준에서 제한이나 잠금을 설정하지 않기 때문에 각 스레드의 프로그램 카운터(PC)는 힙 메모리에 있는 Bean 인스턴스의 동일한 참조를 가리킬 수 있습니다.
따라서 두 스레드 모두 getProdcutById() 메서드를 동시에 실행할 수 있습니다.
다음으로 싱글톤 빈이 무상태를 유지해야 하는 것이 왜 중요한지 알아봅시다.
4. 무상태 싱글톤빈 vs. 유상태 싱글톤 빈
무상태 싱글톤 빈이 왜 중요한지 이해하기 위해 유상태 싱글톤 빈을 사용할 때의 부작용이 무엇인지 살펴보겠습니다.
@Service
public class ProductService {
private String productName = null;
// ...
public Optional getProductById(int id) {
// ...
productName = product.map(Product::getName).orElse(null);
// ...
}
}
여기서, productId 1에 대한 호출의 결과로 "Product 1" 대신 "Product 2"를 표시합니다.
ProductService가 상태를 저장하고 실행 중인 모든 스레드와 동일한 productName 변수를 공유하기 때문에 발생하는 현상입니다.
이와 같은 원치 않는 부작용을 방지하려면 싱글톤 빈을 상태 비저장 상태로 유지하는 것이 중요합니다.
5. 결론
출처 :
https://www.baeldung.com/spring-singleton-concurrent-requests
thread-safe 한 싱글톤 만들기 :
https://seunghyunson.tistory.com/28
'JAVA' 카테고리의 다른 글
스프링 자동설정과 @Profile (5) | 2022.10.26 |
---|---|
[Spring] LocalDateTime response가 array 형식으로 반환될 때 (4) | 2022.08.22 |
[Spring + Redis] 오류 org.springframework.data.redis.serializer.SerializationException: Cannot deserialize; (0) | 2022.07.29 |
[Spring + Redis] 레디스로 게시글 조회수 중복 카운팅 검증하기 (1) | 2022.07.27 |
ifPresent() 메서드로 Optional 처리하기 (7) | 2022.07.22 |