Understanding Spring Bean Scopes and Thread Safety: Singleton vs Prototype

This article explains Spring's bean scopes, compares singleton and prototype beans regarding thread safety, demonstrates the issue with shared state through code examples, and provides practical solutions such as using prototype scope or ThreadLocal to avoid concurrency problems.

Architect's Alchemy Furnace
Architect's Alchemy Furnace
Architect's Alchemy Furnace
Understanding Spring Bean Scopes and Thread Safety: Singleton vs Prototype

Today a colleague asked a question about solving a concrete problem by understanding the underlying principle, so we analyze Spring container bean scopes and thread safety.

Spring bean scopes:

singleton: single instance (default)

prototype: a new instance each request

request: new instance per HTTP request (WebApplicationContext only)

session: one instance per HTTP session

global-session: one instance shared across all sessions

Thread safety must be considered for singleton and prototype beans.

1. Singleton beans are shared by all threads; they are thread‑safe only when stateless (e.g., typical Spring MVC controllers, services, DAOs). If a singleton holds mutable state, concurrent access can cause race conditions.

2. Prototype beans are created anew for each request, so they do not share state and are inherently thread‑safe.

For stateful beans, Spring recommends using ThreadLocal (e.g., RequestContextHolder, TransactionSynchronizationManager, LocaleContextHolder) or explicit locking, though locking can be costly under high concurrency.

Demonstration with a controller

package com.pilot.springbootdemo.controller;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
 * @author wengl
 * @date 2020/08/18 22:56
 * @description Example controller
 */
@Controller
public class ScopeTestController {
    private int num = 0;
    @RequestMapping("/testScope")
    public void testScope() {
        System.out.println(++num);
    }
    @RequestMapping("/testScope2")
    public void testScope2() {
        System.out.println(++num);
    }
}

Calling http://localhost:8080/testScope returns 1, then http://localhost:8080/testScope2 returns 2, showing the singleton controller shares the num field and is not thread‑safe.

After adding @Scope("prototype"):

package com.pilot.springbootdemo.controller;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
 * @author wengl
 * @date 2020/08/18 22:58
 * @description Example controller with prototype scope
 */
@Controller
@Scope("prototype")
public class ScopeTestController {
    private int num = 0;
    @RequestMapping("/testScope")
    public void testScope() {
        System.out.println(++num);
    }
    @RequestMapping("/testScope2")
    public void testScope2() {
        System.out.println(++num);
    }
}

Now both http://localhost:8080/testScope and http://localhost:8080/testScope2 return 1, confirming that each request gets a separate instance.

Solutions

Avoid defining mutable member variables in controllers.

If a non‑static member variable is required, declare the controller with @Scope("prototype") to obtain a new instance per request.

Use ThreadLocal variables in controllers when per‑thread state is needed.

These practices help prevent thread‑safety issues in Spring MVC applications.

JavaSpringThread SafetySingletonprototypeBean Scope
Architect's Alchemy Furnace
Written by

Architect's Alchemy Furnace

A comprehensive platform that combines Java development and architecture design, guaranteeing 100% original content. We explore the essence and philosophy of architecture and provide professional technical articles for aspiring architects.

0 followers
Reader feedback

How this landed with the community

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.