Fundamentals 10 min read

Design and Implementation of a Multithreaded Task Class in Java for Performance Testing

This article explains the design and implementation of a flexible, extensible Java multithreaded task class for performance testing, covering the choice of Runnable over Thread, execution control mechanisms, data collection methods, and sample abstract code illustrating before, test, and after lifecycle hooks.

FunTester
FunTester
FunTester
Design and Implementation of a Multithreaded Task Class in Java for Performance Testing

3.3 Multithreaded Task Class

In the thread model, a fixed number of multithreaded tasks are created and submitted to a thread pool for execution. Therefore, the core of the testing framework includes a multithreaded class that not only runs test tasks but also collects and processes test data.

Below we decompose the multithreaded class by designing and implementing each function.

3.3.1 Multithreading Implementation Approach

In Chapter 1 we introduced the two common Java multithreading techniques: extending the Thread class or implementing the Runnable interface.

We choose to implement Runnable for the following reasons:

Flexibility : Implementing Runnable makes the task class more flexible, while extending Thread limits extensibility.

Ease of use : Implementing Runnable lets us focus on the run() method, whereas extending Thread requires handling its fields and methods.

High extensibility : Implementing Runnable allows inheritance from other parent classes for code reuse.

Additionally, the task class is designed as an abstract class to allow future extensions.

3.3.2 Execution Logic

Two levels of execution control are defined: per‑thread task control and global task control. They are interrelated; an exception in any thread or reaching the test goal can terminate the whole test.

Design includes:

Each instance has a boolean switch indicating whether the current thread should stop.

A thread‑safe AtomicBoolean as a global abort switch.

Methods before() , test() , and after() to define pre‑processing, test execution, and post‑processing.

Termination conditions are typically based on limiting test count or test duration.

package org.funtester.performance.books.chapter03.section3;

import java.util.concurrent.atomic.AtomicBoolean;

/**
 * Multithreaded task class
 */
public abstract class ThreadTask implements Runnable {
    /** Global abort switch */
    public static AtomicBoolean ABORT = new AtomicBoolean(false);
    /** Per‑task stop switch */
    public boolean needStop = false;
    /** Total number of executions */
    public int totalNum;
    @Override
    public void run() {
        before(); // pre‑process
        while (true) {
            if (ABORT.get() || needStop) {
                // check abort
                break;
            }
            try {
                test(); // test method
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        after(); // post‑process
    }
    public abstract void before();
    public abstract void test();
    public abstract void after();
}

The class serves as a foundation for other task classes.

3.3.3 Test Data Handling

Performance testing focuses on response time and execution metrics (total count, success rate, error rate, etc.). Data is collected per thread and aggregated after the test.

Response time collection uses a java.util.List of Integer values; thread‑unsafe collections can be used locally and later merged with a thread‑safe structure.

Key fields added to the class:

/** Execution count */
public int executeNum = 0;

/** Error count */
public int errorNum = 0;

/** List of all request times */
public List
costTime;

The run() method records execution count, start/end timestamps, and stores elapsed time in costTime , while incrementing errorNum on exceptions.

@Override
public void run() {
    before();
    while (true) {
        if (ABORT.get() || needStop || executeNum >= totalNum) {
            break;
        }
        try {
            executeNum++;
            long start = System.currentTimeMillis();
            test();
            long end = System.currentTimeMillis();
            costTime.add((int) (end - start));
        } catch (Exception e) {
            errorNum++;
            e.printStackTrace();
        }
    }
    after();
}

Data aggregation is performed in after() (pseudo‑code shown).

public void after() {
    // aggregate data here
}

The article concludes that while real‑time data processing can affect performance, the presented simple metrics suffice for demonstration, and more sophisticated monitoring can be added as needed.

javaperformance testingthreadpoolMultithreadingAbstract ClassAtomicBoolean
FunTester
Written by

FunTester

10k followers, 1k articles | completely useless

0 followers
Reader feedback

How this landed with the community

login 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.