How Does the Spring Singleton Bean Serve Concurrent Requests? – Spring Singleton Bean是如何服务于并发请求的?

最后修改: 2022年 4月 9日

1. Overview


In this tutorial, we’ll learn how Spring beans created with the singleton scope work behind the scenes to serve multiple concurrent requests. Furthermore, we’ll understand how Java stores the bean instances in memory and how it handles concurrent access to them.

在本教程中,我们将学习使用singleton范围创建的Spring Bean如何在幕后工作,以服务于多个并发请求。此外,我们将了解Java如何在内存中存储Bean实例,以及如何处理对它们的并发访问。

2. Spring Beans and Java Heap Memory

2.Spring Bean和Java堆内存

The Java heap, as we know, is a globally shared memory accessible to all the running threads within an application. When the Spring container creates a bean with the singleton scope, the bean is stored in the heap. This way, all the concurrent threads are able to point to the same bean instance.

我们知道,Java 堆是一个全局共享的内存,可供应用程序中的所有运行线程访问。当Spring容器以单例范围创建Bean时,该Bean被存储在堆中。这样,所有并发的线程都能指向同一个Bean实例。

Next, let’s understand what the stack memory of a thread is and how it helps to serve concurrent requests.


3. How Are Concurrent Requests Served?


As an example, let’s take a Spring application that has a singleton bean called ProductService:


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 =
          .filter(p -> p.getId() == id)
        String productName =

        System.out.printf("Thread: %s; bean instance: %s; product id: %s has the name: %s%n", currentThread().getName(), this, id, productName);

        return product;

This bean has a method getProductById() which returns product data to its callers. Further, the data returned by this bean is exposed to the clients on the endpoint /product/{id}.

这个Bean有一个方法getProductById(),它向其调用者返回产品数据。此外,该Bean返回的数据在端点 /product/{id}上暴露给客户端。

Next, let’s explore what happens at runtime when simultaneous calls hit the endpoint /product/{id}. Specifically, the first thread will call the endpoint /product/1 and the second thread will call /product/2.

接下来,让我们来探讨一下,当同时调用端点 /product/{id}时,在运行时发生了什么。具体来说,第一个线程将调用端点/product/1,第二个线程将调用/product/2

Spring creates a different thread for each request. As we see in the console output below, both threads use the same ProductService instance to return the product data:


Thread: pool-2-thread-1; bean instance: com.baeldung.concurrentrequest.ProductService@18333b93; product id: 1 has the name: Product 1
Thread: pool-2-thread-2; bean instance: com.baeldung.concurrentrequest.ProductService@18333b93; product id: 2 has the name: Product 2

It’s possible for Spring to use the same bean instance in multiple threads, firstly because for each thread, Java creates a private stack memory.


The stack memory is responsible for storing the states of the local variables used inside methods during thread execution. This way, Java makes sure that threads executing in parallel do not overwrite each other’s variables.


Secondly, because ProductService bean sets no restrictions or locks at the heap level, the program counter of each thread is able to point to the same reference of the bean instance in the heap memory. Therefore, both threads can execute the getProdcutById() method simultaneously.

其次,由于ProductServicebean在堆级别没有设置任何限制或锁,每个线程的程序计数器能够指向堆内存中bean实例的同一个引用。因此,两个线程可以同时执行getProdcutById() 方法。

Next, we’ll understand why it’s crucial for singleton beans to be stateless.


4. Stateless Singleton Beans vs. Stateful Singleton Beans


To understand why stateless singleton beans are important, let’s see what the side effects of using stateful singleton beans are.


Suppose we moved the productName variable to the class level:


public class ProductService {
    private String productName = null;
    // ...

    public Optional getProductById(int id) {
        // ...

        productName =;

       // ...

Now, let’s run the service again and look at the output:


Thread: pool-2-thread-2; bean instance: com.baeldung.concurrentrequest.ProductService@7352a12e; product id: 2 has the name: Product 2
Thread: pool-2-thread-1; bean instance: com.baeldung.concurrentrequest.ProductService@7352a12e; product id: 1 has the name: Product 2

As we can see, the call for productId 1 shows the productName “Product 2” instead of “Product 1”. This happens because the ProductService is stateful and shares the same productName variable with all running threads.

我们可以看到,对productId 1的调用显示了productName“产品2 “而不是 “产品1″。这是因为ProductService是有状态的,并且与所有运行的线程共享同一个productName变量。

To avoid undesired side effects like this, it’s crucial to keep our singleton beans stateless.


5. Conclusion


In this article, we saw how concurrent access to singleton beans works in Spring. Firstly, we looked at how Java stores the singleton beans in the heap memory. Next, we learned how different threads access the same singleton instance from the heap. Finally, we discussed why having stateless beans is important, and looked at an example of what can happen if the beans are not stateless.

在这篇文章中,我们看到了Spring中对singleton beans的并发访问是如何进行的。首先,我们研究了Java如何在堆内存中存储单子Bean。接下来,我们了解了不同的线程如何从堆中访问同一个单子实例。最后,我们讨论了为什么拥有无状态Bean很重要,并看了一个例子,说明如果Bean不是无状态的,会发生什么。

As always, the code for these examples is available over on GitHub.