Thread safety in Spring beans depends on their scope. Spring supports multiple bean scopes, with Singleton and Prototype being the most commonly used.
By default, Spring beans are singletons, meaning a single instance exists per Spring IoC container. In contrast, prototype-scoped beans create a new instance each time they are requested from the container.
Prototype beans are inherently thread-safe because each thread operates on a separate instance, eliminating shared state concerns.
Singleton beans may face thread safety issues, but this is not guaranteed—it hinges on whether the bean contains shared mutable state. For example:
@Service
public class CounterService {
private int counter = 0;
public int increase() {
return ++counter;
}
}
In this singleton bean, the counter field is shared across threads. Concurrent invocations of increase() can lead to inocrrect values, making it non-thread-safe. Such beans are stateful, requiring explicit thread safety measures.
Stateless singleton beans, which lack mutable instance variables or have read-only fields, are thread-safe. For instance:
@Service
public class CalculatorService {
public int incrementValue(int value) {
return value + 1;
}
}
In summary:
- Prototype bean are thread-safe.
- Stateless singleton beans are thread-safe.
- Stateful singleton beans are not thread-safe.
Ensuring Thread Safety for Stateful Beans
-
Change Scope to Prototype Setting the bean scope to prototype avoids thread safety issues by providing isolated instances.
@Scope("prototype") @Service public class CounterService { private int counter = 0; // Additional methods }Note that prototype beans incur performance overhead due to frequent instance creation and increased memory usage.
-
Apply Synchronization Using locks, such as the
synchronizedkeyword, can enforce thread safety but may reduce concurrency and system throughput.@Service public class CounterService { private int counter = 0; public synchronized int increase() { return ++counter; } } -
Utilize Concurrent Utilities Java’s concurrency utilities, like atomic classes, offer thread-safe operations with better performance.
import java.util.concurrent.atomic.AtomicInteger; @Service public class CounterService { private AtomicInteger counter = new AtomicInteger(0); public int increase() { return counter.incrementAndGet(); } }This approach is recommended for balancing thread safety and efficiency.