When to Use volatile with ConcurrentHashMap and Spring Bean Thread Safety
A ConcurrentHashMap is internally thread‑safe, so a volatile declaration is only required when its reference may be reassigned (or the field is final), while Spring singleton beans with mutable fields are not thread‑safe and should be made stateless, prototype‑scoped, or synchronized for composite operations.
In a multi‑threaded environment, the interview question asks whether a ConcurrentHashMap (CHM) should be declared volatile to ensure thread safety.
CHM itself provides thread‑safe operations, but its safety only covers the internal methods. If the reference to the CHM never changes, no volatile is needed.
Example of immutable reference:
private static final ConcurrentHashMap
chm = new ConcurrentHashMap<>();If the CHM reference can be reassigned, volatile (or final) is required so that other threads see the new instance.
private volatile ConcurrentHashMap
cache = new ConcurrentHashMap<>();
public void updateCache() {
ConcurrentHashMap
newCache = new ConcurrentHashMap<>();
// fill newCache ...
cache = newCache;
}The same reasoning applies to Spring beans. A singleton bean with mutable state (e.g., an int field) is not thread‑safe; a prototype bean or a stateless singleton is.
@Controller
public class TestController {
private int num = 0;
@RequestMapping("/test")
public void test() { System.out.println(++num); }
@RequestMapping("/test1")
public void test1() { System.out.println(++num); }
}Changing the bean scope to prototype makes each request get a new instance, eliminating shared mutable state.
When a CHM is stored in a singleton controller, the map itself is thread‑safe, but reassigning the map reference without volatile can cause visibility issues.
@RestController
public class TestController {
private ConcurrentHashMap chm = new ConcurrentHashMap();
@RequestMapping("/test")
public void test() { chm.put("1", "1"); }
@RequestMapping("/test1")
public void test1() { chm.put("2", "2"); }
}Solutions include adding volatile , declaring the field final , or using prototype scope.
Composite operations on CHM (e.g., get‑then‑put) are not atomic; external synchronization may be required, as shown in Spring’s SimpleAliasRegistry implementation.
Similarly, Redis get‑set sequences are not atomic, which is why commands like INCR exist.
The article also contains a personal anecdote about playing billiards, which is unrelated to the technical discussion.
Java Tech Enthusiast
Sharing computer programming language knowledge, focusing on Java fundamentals, data structures, related tools, Spring Cloud, IntelliJ IDEA... Book giveaways, red‑packet rewards and other perks await!
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.