[Spring] 싱글톤 빈이 동시 요청을 처리하는 방법

2022. 8. 11. 17:36JAVA

반응형

이 글은 '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은 각 요청에 대해 다른 스레드를 생성합니다.
아래 콘솔 출력에서 ​​볼 수 있듯이 두 스레드 모두 동일한 ProductService 인스턴스를 사용하여 제품 데이터를 반환합니다.

콘솔 출력

 

콘솔 출력으로 미루어 보았을 때, Spring은 여러 스레드에서 동일한 Bean 인스턴스를 사용할 수 있습니다.

각 스레드에 대해 Java가 각각 스택 메모리를 생성하기 때문입니다.

 

스택 메모리는 스레드 실행 중에 메서드 내부에서 사용되는 지역 변수의 상태를 저장하는 역할을 합니다.

이런 식으로 Java는 병렬로 실행되는 스레드가 서로의 변수를 덮어쓰지 않도록 합니다.

 

두 번째로, 'ProductService' 빈은 힙 수준에서 제한이나 잠금을 설정하지 않기 때문에 각 스레드의 프로그램 카운터(PC)는 힙 메모리에 있는 Bean 인스턴스의 동일한 참조를 가리킬 수 있습니다.

따라서 두 스레드 모두 getProdcutById() 메서드를 동시에 실행할 수 있습니다.

 


 

다음으로 싱글톤 빈이 무상태를 유지해야 하는 것이 왜 중요한지 알아봅시다.

 

4. 무상태 싱글톤빈 vs. 유상태 싱글톤 빈

무상태 싱글톤 빈이 왜 중요한지 이해하기 위해 유상태 싱글톤 빈을 사용할 때의 부작용이 무엇인지 살펴보겠습니다.

productName 변수를 클래스 변수로 이동했다고 가정합니다.

 

@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. 결론

 

Spring에서 싱글톤 빈에 대한 동시 접근이 어떻게 이루어지는지 살펴보았습니다.
먼저 Java가 힙 메모리에 싱글톤 빈을 저장하는 방법을 살펴보았고
다음으로 서로 다른 스레드가 힙에서 동일한 싱글톤 인스턴스에 액세스하는 방법을 배웠습니다.
마지막으로 stateless 빈을 갖는 것이 중요한 이유에 대해 살펴보고 빈이 stateless가 아닌 경우 어떤 일이 발생할 수 있는지에 대한 예를 살펴보았습니다.
싱글톤 -> 꼭 무상태

 

 

 

 

 

 

 

 

출처 :

https://www.baeldung.com/spring-singleton-concurrent-requests

 

How Does the Spring Singleton Bean Serve Concurrent Requests? | Baeldung

Learn how Spring beans created with the singleton scope work behind the scenes to serve multiple concurrent requests.

www.baeldung.com

 

 

thread-safe 한 싱글톤 만들기 :

https://seunghyunson.tistory.com/28

 

멀티스레드 환경에서 Thread Safe 하게 Singleton Pattern 사용하기

일반적으로 단일 스레드 환경에서 Singleton Pattern을 사용할 때는 큰 문제가 없습니다. 하지만 멀티스레드 환경에서는 다중 스레드를 이용한다는 부분에서 생길 수 있는 문제점들이 있으며, 이를

seunghyunson.tistory.com

 

반응형