Achieving Thread‑Safe Java Performance Tests Without Locks
This article explains why traditional locking harms Java performance‑testing throughput, introduces lock‑free alternatives such as CAS and ThreadLocal, and provides a complete Java demo that shows how each thread can hold its own object to eliminate contention and boost speed.
Thread safety and lock‑free alternatives
In Java performance testing, using synchronized blocks or explicit locks can add considerable overhead. Two lock‑free approaches are common: Compare‑And‑Swap (CAS) and ThreadLocal. This article focuses on ThreadLocal.
How ThreadLocal works
ThreadLocal maintains a separate value for each thread. When a thread first calls get(), the initialValue() method is invoked to create the value, which is then stored in a map keyed by the thread. Subsequent calls return the same instance for that thread, eliminating shared mutable state and therefore removing the need for synchronization.
Typical use cases include objects that are expensive to create or that occupy large amounts of memory, and where each thread can safely work with its own copy.
Demo program
The following self‑contained example shows a ThreadLocal that holds a String created from a timestamp (via getNanoMark()) and an empty constant. The hash code of each created string is printed so that the distinct instances can be observed.
package com.fun;
import com.fun.frame.SourceCode;
public class AR extends SourceCode {
// ThreadLocal that creates a new String for each thread
static ThreadLocal<String> local = new ThreadLocal<String>() {
@Override
public String initialValue() {
String s = new String(getNanoMark() + EMPTY);
output(s.hashCode()); // print hash to identify the instance
return s;
}
};
public static void main(String[] args) throws InterruptedException {
AR ar = new AR();
// Calls in the main thread
ar.ss();
ar.ss();
// Two additional threads that also invoke ss()
Thread t1 = new Thread(() -> ar.ss());
Thread t2 = new Thread(() -> ar.ss());
t1.start();
t2.start();
t1.join();
t2.join();
}
// Access the ThreadLocal value
public void ss() {
local.get();
}
}Expected output
Each thread prints a different hash code, confirming that separate objects were created:
INFO-> 851397249
INFO-> 1851572413
INFO-> 1875674350
Process finished with exit code 0Practical considerations
ThreadLocal eliminates contention but does not free the stored objects until the thread terminates. In thread‑pool environments you should call local.remove() when the value is no longer needed to avoid memory leaks.
The initial value is created lazily; if the creation is expensive, the cost is incurred only once per thread.
ThreadLocal is appropriate when the object does not need to be shared across threads. If cross‑thread communication is required, other concurrency primitives must be used.
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.
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.
