How to Fix Empty Mark Records in Java Performance Tests with Thread‑Safe Save
The article explains why using synchronized(this.getClass()) in anonymous test threads can produce empty mark records during performance testing, and shows how replacing it with a static synchronized save method resolves the thread‑safety issue.
During performance testing the author created two virtual classes ThreadLimitTimeCount and ThreadLimitTimesCount to mark threads and record timeout requests. The original after() method used a synchronized block on this.getClass() to write the collected marks:
@Override
protected void after() {
requestMark.addAll(marks);
marks = new ArrayList<>();
GCThread.stop();
synchronized (this.getClass()) {
if (countDownLatch.getCount() == 0 && requestMark.size() != 0) {
Save.saveStringList(requestMark, MARK_Path.replace(LONG_Path, EMPTY) + Time.getDate().replace(SPACE_1, CONNECTOR));
requestMark = new Vector<>();
}
}
}In unit tests that use anonymous classes, the clone() method is unavailable, preventing deep copies of the objects. Consequently, multiple anonymous thread classes are created, each with its own runtime class object. Synchronizing on this.getClass() therefore locks only the specific anonymous class instance, not all of them, leading to occasional empty markrequest records.
To guarantee proper thread safety across all anonymous classes, the author replaced the class‑level lock with a dedicated synchronized save method. The new utility method synchronizes on the Save class itself:
/**
* Synchronously save data, used by multiple anonymous test threads.
* @param data the collection of strings to save
* @param name the file name
*/
public static void saveStringListSync(Collection<String> data, String name) {
synchronized (Save.class) {
if (data.isEmpty()) return;
saveStringList(data, name);
}
}The after() method is then simplified to call this thread‑safe method:
if (countDownLatch.getCount() == 0 && requestMark.size() != 0) {
Save.saveStringListSync(requestMark, MARK_Path.replace(LONG_Path, EMPTY) + Time.getDate().replace(SPACE_1, CONNECTOR));
}By synchronizing on a single, shared class object ( Save.class), all threads—regardless of which anonymous class created them—contend for the same lock, eliminating the race condition that produced empty mark files.
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.
