Why Spring MVC Controllers Are Singleton and How to Ensure Thread Safety

This article explains that Spring MVC controllers are singleton by default, illustrates the thread‑safety problems caused by shared instance variables with concrete code examples, and provides practical solutions such as avoiding state, using prototype scope, ThreadLocal, or AtomicInteger to make controllers safe under high concurrency.

Programmer DD
Programmer DD
Programmer DD
Why Spring MVC Controllers Are Singleton and How to Ensure Thread Safety

Singleton pattern is a key design pattern often examined in Java interviews; a common question is whether Spring MVC controllers are singleton or prototype.

Each incoming request requires a thread for its duration. If more simultaneous requests arrive than available threads, Tomcat creates additional threads up to maxThreads ; excess requests are queued up to acceptCount , after which connections are refused.

In a Tomcat container each servlet is a singleton, and Spring MVC controllers are singleton by default. Using the singleton pattern saves memory and improves resilience under high concurrency.

Controller is not thread‑safe

Because the controller is a singleton, instance variables are shared across threads. Defining mutable fields can lead to race conditions and break idempotency.

@Controller
public class TestController {
    private int num = 0;

    @RequestMapping("/addNum")
    public void addNum() {
        System.out.println(++num);
    }
}

When running locally, the first request to /addNum returns 1, the second returns 2, showing that the shared num variable is modified by each request, breaking the expected behavior.

All requests access the same controller instance, so the private member variable is shared among threads.

A thread that modifies the variable affects the value seen by other concurrent requests.

Solutions for controller concurrency safety

To guarantee thread safety, consider the following approaches:

Avoid defining instance variables in the controller.

If a non‑static variable is necessary, annotate the controller with @Scope("prototype") to make it prototype scoped.

Use a ThreadLocal variable so each thread has its own copy.

Define the variable as AtomicInteger and use its atomic operations.

@Controller
@Scope("prototype")
public class TestController {
    private int num = 0;

    @RequestMapping("/addNum")
    public void addNum() {
        System.out.println(++num);
    }
}
public class TestController {
    private int num = 0;
    private final ThreadLocal<Integer> uniqueNum = new ThreadLocal<Integer>() {
        @Override
        protected Integer initialValue() {
            return num;
        }
    };

    @RequestMapping("/addNum")
    public void addNum() {
        int unum = uniqueNum.get();
        uniqueNum.set(++unum);
        System.out.println(uniqueNum.get());
    }
}

After applying these techniques, each request to /addNum consistently returns the same result, demonstrating proper thread‑safe behavior. In summary, avoid mutable instance variables in controllers whenever possible.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Javaconcurrencythread safetyControllerSpring MVCSingleton
Programmer DD
Written by

Programmer DD

A tinkering programmer and author of "Spring Cloud Microservices in Action"

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.