Backend Development 8 min read

Ensuring Thread Safety for Spring Singleton Beans: Problems and Solutions

This article explains why Spring singleton beans can be unsafe in concurrent scenarios, demonstrates the issue with a controller example, and presents multiple solutions including changing bean scope, using ThreadLocal, avoiding member variables, employing concurrent collections, and leveraging distributed caches.

Code Ape Tech Column
Code Ape Tech Column
Code Ape Tech Column
Ensuring Thread Safety for Spring Singleton Beans: Problems and Solutions

Spring beans are singleton by default, which can cause concurrency issues when member variables are used in Controllers because multiple requests share the same instance.

Example code shows a singleton controller with an int field that increments on each request, demonstrating unsafe behavior.

@Controller
public class HomeController {
    private int i;
    @GetMapping("testsingleton1")
    @ResponseBody
    public int test1() {
        return ++i;
    }
}

To achieve stateless handling of massive HTTP requests, several solutions are presented:

1. Change bean scope to prototype or request

Annotate the controller with @Scope("prototype") or @Scope("request") (or on non‑web components use @Scope("prototype") ), which creates a new instance per request but increases resource consumption.

2. Use ThreadLocal for thread isolation

Wrap the member variable in a ThreadLocal to keep a separate value per thread, as shown below, and log the thread name and value.

@Controller
public class HomeController {
    private ThreadLocal
i = new ThreadLocal<>();
    @GetMapping("testsingleton1")
    @ResponseBody
    public int test1() {
        if (i.get() == null) {
            i.set(0);
        }
        i.set(i.get() + 1);
        log.info("{} -> {}", Thread.currentThread().getName(), i.get());
        return i.get();
    }
}

Log output shows each thread maintains its own counter, but the approach still does not guarantee overall concurrency safety.

3. Avoid member variables

Prefer using method‑local variables instead of fields, which is the simplest and recommended way.

@Controller
public class HomeController {
    @GetMapping("testsingleton1")
    @ResponseBody
    public int test1() {
        int i = 0;
        // TODO business code
        return ++i;
    }
}

4. Use concurrent collections such as ConcurrentHashMap when a shared mutable state is required.

5. For distributed systems, store shared state in external caches like Redis.

Finally, the article lists the five Spring bean scopes (singleton, prototype, request, session, global session) and briefly describes each.

BackendSpringThread SafetythreadlocalsingletonBean Scope
Code Ape Tech Column
Written by

Code Ape Tech Column

Former Ant Group P8 engineer, pure technologist, sharing full‑stack Java, job interview and career advice through a column. Site: java-family.cn

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.