ThreadLocal & ConcurrentHashMap: Hidden Bugs in Java Concurrency
This article examines common misconceptions when using Java concurrency utilities such as ThreadLocal, ConcurrentHashMap, and CopyOnWriteArrayList, demonstrates real‑world bugs caused by thread reuse and improper atomic operations, and provides practical solutions—including explicit cleanup, proper locking, and high‑performance alternatives like computeIfAbsent and LongAdder—to ensure thread‑safe and efficient code.
Thread reuse causing user information mix‑up
In production, a request sometimes receives another user’s information because the user data is cached in a ThreadLocal. Tomcat reuses worker threads from a pool; if a thread is reused without clearing the ThreadLocal, the next request inherits stale data.
Reproduce the bug by setting Tomcat’s max‑threads to 1, then sending two requests sequentially. The first request gets null then 1 as expected; the second request gets 1 on the first read (stale data) and 2 on the second.
Solution : always clear the ThreadLocal in a finally block so that reused threads start with a clean state.
ThreadLocalRandom usage
When ThreadLocalRandom.current() is called, a seed is stored in the current thread. Other threads cannot see this seed, so each thread must initialize its own seed.
UNSAFE.putLong(t = Thread.currentThread(), SEED,
r = UNSAFE.getLong(t, SEED) + GAMMA); UNSAFE.getLong(Thread.currentThread(), SEED);Is ConcurrentHashMap really safe?
ConcurrentHashMap guarantees thread‑safe atomic read/write operations, but compound actions (e.g., size(), putAll()) are not atomic.
Bug case
A map initially contains 900 entries. Ten threads concurrently try to add 100 more entries by reading size(), computing the missing count, and calling putAll(). The final size ends up at 1549 instead of the expected 1000 because the operations are not atomic.
Analysis
ConcurrentHashMap does not guarantee consistency across multiple method calls; manual locking is required for such sequences.
Aggregating methods like size(), isEmpty(), containsValue() may return intermediate states and should not be used for control flow. putAll is not atomic; partial updates can be observed by other threads.
Solution
Wrap the whole logic in a lock, or better, use atomic methods such as computeIfAbsent together with a thread‑safe counter like LongAdder.
High‑performance computeIfAbsent
Using computeIfAbsent to create a LongAdder for each key allows lock‑free increments. This yields at least a 5× performance gain over explicit locking.
static final <K,V> boolean casTabAt(Node<K,V>[] tab, int i,
Node<K,V> c, Node<K,V> v) {
return U.compareAndSetObject(tab, ((long)i << ASHIFT) + ABASE, c, v);
}CopyOnWriteArrayList pitfalls
CopyOnWriteArrayList is thread‑safe but copies the entire array on each write, making it unsuitable for write‑heavy scenarios. Benchmarks show it is hundreds of times slower than a synchronized ArrayList for concurrent writes, though it excels at read‑heavy workloads.
Performance comparison
Concurrent writes: CopyOnWriteArrayList is ~100× slower than synchronized ArrayList.
Concurrent reads (1 M get operations): CopyOnWriteArrayList is ~24× faster.
Takeaways
Don’t
Rely on concurrency utilities without understanding thread fundamentals.
Assume a tool guarantees thread safety for all usage patterns.
Ignore the performance characteristics of the chosen tool.
Select a tool without considering the specific business scenario.
Do
Read the official documentation, understand the applicable scenarios and API semantics, and perform thorough testing and performance benchmarking before deploying concurrency code in production.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Programmer DD
A tinkering programmer and author of "Spring Cloud Microservices in Action"
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.
