Operations 4 min read

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.

FunTester
FunTester
FunTester
How to Fix Empty Mark Records in Java Performance Tests with Thread‑Safe Save

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.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaPerformance TestingSynchronizationthread safetyAnonymous Classes
FunTester
Written by

FunTester

10k followers, 1k articles | completely useless

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.