Common Concurrency Bugs in Backend Java Development and Their Solutions
Although developers often overlook high‑concurrency scenarios, this article highlights two typical backend Java concurrency bugs—shared request state in singleton beans and improper lazy initialization—explains their pitfalls, and demonstrates correct thread‑safe patterns such as double‑checked locking and proper singleton design.
Ideally, developers should consider concurrency before writing code, but in most real cases they don't consider high‑concurrency business issues because such load is hard to encounter.
Below are two common concurrency bugs in backend coding:
Request State in Bean
In Java applications, servers, controllers, handlers, and repositories are usually singletons. They are created at application startup, and requests are typically passed to them by multiple threads.
Code is as follows:
public void handleOrder(Order order) {
...
currentLineItem = order.getLine(0);
processLineItem();
}
private void processLineItem() {
myService.store(currentLineItem);
}This violates two principles: thread safety and meaningful object state. When handling an order object, only one of its line items ( currentLineItem ) is processed. The line item is assigned to a field of the singleton, then processed. If multiple threads invoke the singleton simultaneously, the field may be overwritten between assignment and processing, causing the processing to use a line item that no longer belongs to the original order.
If each request's attributes are placed into a request‑specific receiver, there are two risks:
Errors between requests in multithreaded execution.
Errors between requests in single‑threaded execution if processing is not fully completed.
Object Initialization Errors
Lazy initialization allows:
Faster startup because resources are loaded only when needed.
If not needed, resources are never loaded (e.g., serverless Lambda may never execute a particular code path).
Prioritizing loading of active resources.
However, the following code may cause errors:
private LazyService getLazyService() {
if (lazyService != null) {
return lazyService;
}
LazyService newLazyService = connectToLazyService();
registerWithServiceRegistry(newLazyService);
lazyService = newLazyService;
return newLazyService;
}Although it can work, concurrent calls are likely to fail. In the example:
Multiple lazy loads occur under concurrent calls.
If multiple lazy loads happen, objects may stay in memory too long or forever.
If the class is a singleton, extra objects created during initialization may acquire exclusive resources and cause malfunction.
To correctly initialize a singleton, you should use double‑checked locking or a framework, or even a simple static‑field Java singleton as follows:
private volatile static Singleton singleton;
public static Singleton getSingleton() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}Disclaimer: This article was first published on the public account “FunTester”; reproduction by third parties (except Tencent Cloud) is prohibited.
The article concludes with a curated list of technical and non‑technical articles for further reading.
FunTester
10k followers, 1k articles | completely useless
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.