How to Build a Custom Multithreaded Test Engine in Java
This article walks through designing and implementing a Java multithreaded execution class for performance testing, covering thread‑pool creation, task coordination with CountDownLatch, start/stop control, and a complete demo that runs concurrent tasks with timed logging.
Overview
This guide shows how to build a reusable multithreaded execution component in Java for performance testing. The component creates a fixed‑size thread pool, records start/end timestamps, tracks execution statistics, and provides explicit start() and stop() methods.
Fixed‑size Thread Pool
public static ThreadPoolExecutor createPool(int size) {
return new ThreadPoolExecutor(
size, size, 60L, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(10),
new ThreadFactory() {
private final AtomicInteger index = new AtomicInteger();
@Override
public Thread newThread(Runnable r) {
Thread thread = new Thread(r);
thread.setName("Thread-" + index.incrementAndGet());
return thread;
}
});
}TaskExecutor fields
ThreadPoolExecutor poolExecutor– the thread pool that runs the tasks. int TaskNum – number of concurrent tasks (optional, derived from the task list). AtomicInteger executeNumStatistic – total number of executions performed. AtomicInteger errorNumStatistic – count of execution errors. Vector<Integer> costTimeStatistic – collection of each execution’s duration (ms). String taskDesc – human‑readable description of the test run. long startTimestamp – millisecond epoch when start() is called. long endTimestamp – millisecond epoch when all tasks finish. List<ThreadTask> tasks – the list of user‑defined tasks. CountDownLatch stopCountDownLatch – synchronises the main thread with task completion.
Constructor
public TaskExecutor(List<ThreadTask> tasks, String taskDesc) {
this.tasks = tasks;
this.taskDesc = taskDesc;
this.executeNumStatistic = new AtomicInteger(0);
this.errorNumStatistic = new AtomicInteger(0);
this.costTimeStatistic = new Vector<>();
this.poolExecutor = ThreadTool.createPool(tasks.size());
this.stopCountDownLatch = new CountDownLatch(tasks.size());
for (int i = 0; i < tasks.size(); i++) {
tasks.get(i).stopCountDownLatch = stopCountDownLatch;
}
}Start and Stop methods
public void start() {
this.startTimestamp = System.currentTimeMillis();
for (ThreadTask task : tasks) {
poolExecutor.execute(task);
ThreadTool.sleep(1000); // 1‑second interval between submissions
}
try {
stopCountDownLatch.await(); // wait until all tasks call countDown()
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
this.endTimestamp = System.currentTimeMillis();
this.poolExecutor.shutdown();
System.out.println(System.currentTimeMillis() + " 性能测试任务执行完毕!");
}
public void stop() {
for (int i = 0; i < tasks.size(); i++) {
tasks.get(i).needStop = true; // signal each task to exit its loop
}
}ThreadTask run method (with CountDownLatch)
@Override
public void run() {
try {
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();
} catch (Exception e) {
e.printStackTrace();
} finally {
stopCountDownLatch.countDown(); // signal completion to TaskExecutor
}
}Demo application
public class TestEngineDemo {
public static void main(String[] args) {
int tasksNum = 2; // number of concurrent threads
int totalNum = 3; // executions per thread
List<ThreadTask> tasks = new ArrayList<>();
for (int i = 0; i < tasksNum; i++) {
ThreadTask threadTask = new ThreadTask() {
@Override
public void before() {
System.out.println(System.currentTimeMillis() + " before testing ! " + Thread.currentThread().getName());
}
@Override
public void test() {
ThreadTool.sleep(500); // simulate work
System.out.println(System.currentTimeMillis() + " testing ! " + Thread.currentThread().getName());
}
@Override
public void after() {
System.out.println(System.currentTimeMillis() + " after testing ! " + Thread.currentThread().getName());
}
};
threadTask.totalNum = totalNum;
threadTask.costTime = new ArrayList<>(totalNum);
tasks.add(threadTask);
}
new TaskExecutor(tasks, "性能测试引擎演示").start();
}
}The demo launches two threads, each performing three test cycles with a 1‑second interval between thread starts. The console output shows timestamps for before(), test(), and after() phases, confirming that the thread pool shuts down after all tasks finish.
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.
