A Spring bean. Sounds relatively harmless, doesn't it? What if I told you that this could be the source of many headaches in web servers, especially if you are new to Spring development. However, if you're a more seasoned Java developer, then you would be aware of the restless nights debugging your code for hours only to discover that the dreaded bug was a . race condition What is a Race Condition? In short, a race condition is when we have simultaneous access from multiple threads/processes on the same resource without some mechanism to process the requests synchronously. This access is fine when all of the requests are only , but it quickly becomes hell when you throw into the mix. reads writes This can be illustrated with a simple example of multiple developers working on the same project. As a software engineer, you would typically work in a team with at least several other people. Sometimes you may all be working on the same file -- adding new features, bugs, and whatnot. Like any good team, you all agreed to use some version control system like Git rather than passing around zip files like a bunch of heathens to manage your codebase and avoid any conflicts. The three developers, Alice, Bob, and Eve, work on the same file modifying different functions. Alice finds that the implementation of her new feature needs to modify some existing method named . Eve also runs into the same requirement and does the same. Bob, on the other hand, is busy on a side quest to fix his build issues. foo() Eve finishes modifying the file and sends out a pull request with the modified method. Bob frustrated with the lack of progress in fixing his issue reviews the code and gives it a thumbs up. Code gets shipped. Alice had lost track of time and is in a rush to ship the feature before midnight. Without any pull request, she pushes straight to production. Luckily given how smart Git is, Git will detect a conflict on push and refuses to add her change in. Alice sees this, rebases, and makes a good decision to call Bob at midnight to review her code before pushing. foo() Now, imagine that this version control system is gone. The world descends to chaos as Alice's very important change overwrites Eve's work producing unexpected behavior as the latest is always treated as the source of truth. push In this situation, Git acts as the mechanism that prevents concurrent writes by keeping track of events and having them be processed in some sort of fashion -- in this case chronological order. In principle, locks and semaphores serve the same purpose in blocking access to all but a single thread if these threads are designed to manipulate data. synchronization Spring Beans and You Taken straight from their website, The Spring Framework provides a comprehensive programming and configuration model for modern Java-based enterprise applications - on any kind of deployment platform. Now a popular variant of the Spring Framework is Spring Boot, a framework that enables developers to create production-grade backend web applications. One of the of Spring is its use of dependency injection at its core. Classes are typically created as , which serve as the of your Spring application. main tenets beans backbone Following its main tenet of dependency injection, beans are created with the scope by default -- meaning that only one copy of a class is instantiated for the lifecycle of the application. Of course, there are many other types of scopes, but we will focus on the singleton scope today. singleton Depiction of Spring Bean life cycle taken from . documentation So why does it matter if it is a singleton? This property matters when you have to handle multiple sources that may be accessing it, especially if web requests are made to modify the state of the singleton bean. . For this scenario, however, Side note: Please do not make your Spring beans stateful, it will save you a lot of headaches along the way let's say you must maintain some state in your Spring bean. The question is, how would a singleton bean handle requests from multiple users? To accomplish this like most web servers, another handler, in this case, a thread, is spawned to serve a request made by a single user. In other words, if two users are making a call to your API, one to the endpoint and the other to the endpoint, Spring will spawn two separate threads to handle these calls. If we have a singleton bean acting as our controller, then both requests will be handled by that bean. This becomes a pain point when we have multiple simultaneous calls to the same endpoint that updates the same variable in a non-thread-safe manner. /register /login With the possibility of simultaneous calls with mutable state, Spring Beans are inherently. not thread-safe An Example As mentioned before, let's use a very basic stateful bean as our example (basically a Java bean that as instance variables). I went ahead and created a to demonstrate a couple of concepts that I will be discussing in the coming sections. The project itself is a simple Spring Boot project with a few endpoints that manipulate data inside our class. sample project DataFacade.java For our first API, I will focus on the API which returns a strictly increasing number to each user. We that no two persons calling the endpoint will have the same number: /increment guarantee // DataController.java snippet @RestController public class DataController { @RequestMapping("/increment") private int incrementEndpoint() { return dataFacade.increment(); } } // DataFacade.java snippet @Component @Scope("singleton") public class DataFacade { private int counter; public int increment() { return ++this.counter; } } In short, our bean stores as an instance variable and is modified by the method. By itself, the class doesn't seem to do much and not much can go wrong with it. You fire up the Spring application, hit the endpoint, and you see gets returned. You call it again and receive a and so on. DataController counter increment() localhost:8080/increment 1 2 What's the big deal here? Let's say we launch this amazing API that guarantees no two persons get the same number generated, gets featured on Product Hunt, and then gets 12,000 daily active users making on average 500 calls per day each, meaning 6,000,000 total calls per day to this endpoint. If we do the math, the API will on average serve (6,000,000 calls per day / 86,400 seconds per day) = 69.44... transactions per second. The users eventually find out that some have received the same number and get pissed off. What the hell happened? What happened was a classic race condition. With a load of ~69 TPS, there is a high chance that multiple users could be calling at the same time with the chance of reading stale data and incrementing with the wrong number. If the counter was at 5 and Alice and Bob call at the same time, then there is a good chance that both will receive 6 when they should be different. /increment /increment The value of is also dependent on the last call that updated its value. In the diagram above, if we had 3 threads incrementing the value in around the same time, then there is a chance that all these threads read as 5, and all end up setting the value at 6 -- which is incorrect. What we should expect is some decision made (usually based on chronological order) for thread A and B to execute it. Thread C will have to wait until both threads A and B are done with updating the variable before it can execute. The expected value for is supposed to be 8. counter counter counter counter Since we only have a single copy of our , every single thread accessing the variable is concurrently modifying its value. Recall that Spring spawns a separate thread for each user accessing the endpoint. Spring offers 0 safeguards to ensure thread safety. DataFacade counter Let's test this using our . sample project First, clone the project to some directory on your machine. Open the directory in your terminal and run the view error-prone code. git checkout race-conditions Then, open the project in an IDE of your choice (IntelliJ is preferred) and run the project. Once started, you should be able to access . localhost:8080/increment Next, open up your terminal and navigate to our test script called and run the following command. spammer.py python spammer.py The script will spawn 4 threads and make 50 calls each to the endpoint. You can change these if you'd like. /increment Once the script finishes, the expected value for is 200 if 4 threads each make 50 calls without any repeating numbers. However, notice that the counter fell short of 200 (176 in our case). The concurrent modification of is evinced by the appearance of repeating numbers in our log (for example below, 174). counter counter ... Thread 0 - 162 Thread 1 - 163 Thread 3 - 164 Thread 1 - 165 Thread 0 - 166 Thread 3 - 168 Thread 2 - 167 Thread 1 - 169 Thread 3 - 171 Thread 0 - 170 Thread 2 - 172 Thread 0 - 173 Thread 1 - 174 Thread 2 - 174 Thread 3 - 175 Thread 1 - 177 Thread 2 - 176 The next section discusses solutions to this issue and changes we can make to the existing code in the project. Making the Bean Thread-Safe Going back to our previous point, singleton beans are . This is because these beans run for the entire lifetime of the application and many HTTP(S) requests are made from different users at the same time. not inherently thread safe So how can we fix that? Request Scoping The is a web-aware ApplicationContext in Spring that creates a new bean instance for every single HTTP request. This scope can be extremely helpful for maintaining some sort of state for that request if that bean will be passed on to many different handlers down the chain. For instance, if your state is stored in some bean, you can add the annotation such that the bean will get created on each HTTP request. request scope @RequestScope @Bean @RequestScope public DataFacade getDataFacade() { // ... } On the surface, making every single bean you use request scoped sounds like a great idea. However, is creating a new bean for every single request really that efficient? Do you need all of your beans to be thread-safe? In general, having to create a new bean will add overhead that will slow down your application's performance. Whether if you want all your beans to be thread-safe is also up to you, but most of the time you also wouldn't need every bean to be thread-safe. Thread-Safe Variables Let's say you have no other option but to keep your bean stateful. Although Spring will not handle synchronization and concurrent modification issues, you can handle it yourself. There are a couple of options on how to do it in Java: - the keyword is a handy way to restrict access to shared resources to only a single thread at a time. You can apply this keyword on either a or an object. Either way, the JVM knows that once a thread acquires a lock to execute code within a synchronized block, all other threads must be suspended. When the lock is released, then the next thread in line can access. Note that the variables accessed by multiple threads must be declared with the keyword to ensure changes to one thread are immediately reflected to the other threads. Synchronized blocks synchronized method volatile With regards to our example project, we can add the keyword to the calling method and the keyword to our integer. Both keywords will not work correctly without the other. synchronized volatile private volatile int counter; // ... public synchronized int increment() { return ++this.counter; } With the and keywords in place, we should see this output. Although the output may not be in order (which is still fine), we reach 200 with our counter without any repeated values. synchronize volatile ... Thread 1 - 185 Thread 2 - 184 Thread 0 - 186 Thread 3 - 187 Thread 1 - 188 Thread 2 - 189 Thread 3 - 191 Thread 0 - 190 Thread 2 - 192 Thread 1 - 193 Thread 3 - 194 Thread 0 - 195 Thread 1 - 197 Thread 2 - 196 Thread 3 - 199 Thread 0 - 198 Thread 1 - 200 The one point of concern with synchronization, however, is its performance impact. Synchronization does take time to process and having all the requests be queued up and processed one at a time rather than concurrently will slow down response times. - using the keyword in the encapsulating block is not enough to prevent race conditions for Java collections. To ensure synchronous access to your collection, the Java collections framework ships with different wrapper methods that transform your collection into its thread-safe version. You can read a great synopsis by for all the different wrapper classes you can use. Synchronized Collections synchronized Baeldung public Set<String> toSynchronizedSet(final Set<Integer> numbers) { return Collections.synchronizedSet(numbers); } In our sample project, the other endpoint that needs fixing is the endpoint. I'll leave that as an exercise for you to fix... or just copy the code from . /addstring master - according to , the package is a . Atomic variables have a lot of utility methods that perform comparisons, increment/decrement, accumulation, etc. without needing to specify and blocks and variables respectively. Atomic Variables Oracle's documentation java.util.concurrent.atomic small toolkit of classes that support lock-free thread-safe programming on single variables synchronized volatile In our endpoint, the can be replaced with an . /increment counter AtomicInteger private AtomicInteger counter; public DataFacade() { this.counter = new AtomicInteger(0); } public int increment() { return this.counter.incrementAndGet(); } Completely Stateless This may be a cop-out answer, but a fool-proof solution to this problem is to never maintain state inside the bean to begin with. This means that your mean would contain any fields, but it is uncommon for developers to build classes like this. However, you can still achieve statelessness in your bean if the execution of its methods does not alter its instance variables. There would be no need to worry about the synchronization of variables, access patterns, and cleaner code. Local variables within methods, however, are not affected by this as these local variables are allocated in the stack and only accessed within the scope of that method. As a side note, variables can also be included while still maintaining statelessness, but with a catch. A final , , or may work fine, but a final , , or will not. Elements of these collection classes can still be changed during code execution which adds state. JDK 9 introduced of these collections, but they must be used with a bit of caution. Objects are not automatically immutable when stored in these collections, only the collection itself is. final String int boolean List Set Collection immutable versions Conclusion The takeaway from all this is that working with singleton beans in a multi-threaded environment without the right mechanisms to ensure thread safety can be an absolute nightmare to debug. Even if it is possible to add thread-safety to your instance variables in the singleton bean itself, the cleaner option may be to only keep constant instance variables and move all changing variables into local methods or other classes that are created on a per-request basis. I hope this post brought greater insight on some of the dangers in maintaining state in your singleton Spring beans. Thanks for reading! 💎 Thank you for taking the time to check out this post. For more content like this, head over to my actual . Feel free to reach out to me on and follow me on . blog LinkedIn Github Also published . here