Dynamic Load Adjustment in FunTester: A Step‑by‑Step Implementation Guide
This article explains how to redesign the FunTester framework to enable real‑time pressure scaling by treating each multithreaded task as a manageable object, adding interrupt capabilities, and implementing global management for dynamic thread addition, removal, and cloning during load testing.
Concept
The framework enables dynamic adjustment of load (thread count) during a performance test. Each multithreaded task is represented by an interrupt‑able object and managed through a global task pool.
Thread Task Refactor
A new subclass com.funtester.base.constaint.FunThread<F> extends ThreadBase. Key additions:
A volatile interrupt flag BREAK_KEY and method interrupt() to stop a thread.
A simplified run() that repeatedly calls doing() while !BREAK_KEY, catching exceptions.
package com.funtester.base.constaint;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.Vector;
public abstract class FunThread<F> extends ThreadBase {
private static final long serialVersionUID = 7878297575504772944L;
private static final Logger logger = LogManager.getLogger();
private static Vector<FunThread> threads = new Vector<>();
private boolean BREAK_KEY = false;
public FunThread(F f, String name) {
this.isTimesMode = true;
this.threadName = name;
this.limit = Integer.MAX_VALUE;
this.f = f;
}
@Override
public void run() {
before();
while (!BREAK_KEY) {
try { doing(); } catch (Exception e) { logger.warn("Execution failed!", e); }
}
}
public void interrupt() { BREAK_KEY = true; }
// abstract FunThread clone() and other utilities are defined elsewhere
}Management Functions
Static synchronized methods on FunThread manipulate the Vector<FunThread> task pool.
public static synchronized void stop() {
threads.forEach(FunThread::interrupt);
threads.clear();
}
public static synchronized boolean addThread(FunThread base) {
if (!checkName(base.threadName)) return false;
return threads.add(base);
}
public static synchronized void remoreThread(FunThread base) {
base.interrupt();
threads.remove(base);
}
public static synchronized FunThread getRandom() {
return random(threads);
}Dynamic Execution Class
The class FunConcurrent creates a cached thread pool, registers a controller, and submits each FunThread to the pool.
package com.funtester.frame.execute;
import com.funtester.base.constaint.FunThread;
import com.funtester.base.interfaces.IFunController;
import com.funtester.config.HttpClientConstant;
import com.funtester.frame.SourceCode;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
public class FunConcurrent extends SourceCode {
private static Logger logger = LogManager.getLogger(FunConcurrent.class);
public List<FunThread> threads = new ArrayList<>();
public static ExecutorService executorService;
public static IFunController controller;
public FunConcurrent(List<FunThread> threads) {
this.threads = threads;
executorService = ThreadPoolUtil.createCachePool(HttpClientConstant.THREADPOOL_MAX);
}
public void start() {
if (controller == null) controller = new FunTester();
new Thread(controller, "Receiver").start();
threads.forEach(FunConcurrent::addTask);
}
public static void addTask(FunThread thread) {
boolean ok = FunThread.addThread(thread);
logger.info("Task {} added {}", thread.threadName, ok ? "success" : "failure");
if (ok) executorService.execute(thread);
}
public static void addTask() {
FunThread thread = FunThread.getRandom();
addTask(thread.clone());
}
public static void removeTask(FunThread thread) {
logger.info("Task {} terminated", thread.threadName);
FunThread.remoreThread(thread);
}
public static void removeTask(String name) {
logger.info("Task {} terminated", name);
FunThread.remoreThread(name);
}
public static void removeTask() {
FunThread thread = FunThread.getRandom();
removeTask(thread);
}
}External‑Factor Controller
The inner class FunTester implements IFunController. It reads keyboard input and triggers: add() – clones a random task and adds it. reduce() – removes a random task. over() – stops all tasks.
private static class FunTester implements IFunController {
private boolean running = true;
@Override
public void run() {
while (running) {
String input = getInput();
switch (input) {
case "+": add(); break;
case "-": reduce(); break;
case "*": over(); running = false; break;
default: break;
}
}
}
@Override public void add() { FunConcurrent.addTask(); }
@Override public void reduce() { FunConcurrent.removeTask(); }
@Override public void over() { logger.info("Dynamic test ended"); FunThread.stop(); }
}Test Script
A minimal main creates two baseline tasks, starts the concurrent runner, and lets the user adjust load via keyboard.
package com.funtest.funthead;
import com.funtester.base.constaint.FunThread;
import com.funtester.frame.SourceCode;
import com.funtester.frame.execute.FunConcurrent;
import java.util.Arrays;
public class Ft extends SourceCode {
public static void main(String[] args) {
FunTester taskA = new FunTester("task A");
FunTester taskB = new FunTester("task B");
new FunConcurrent(Arrays.asList(taskA, taskB)).start();
}
private static class FunTester extends FunThread {
public FunTester(String name) { super(null, name); }
@Override protected void doing() throws Exception {
sleep(3.0 + getRandomDouble());
output(threadName + "\tTask is running!");
}
@Override public FunThread clone() {
return new FunTester(this.threadName + "Clone");
}
}
}Console Output Highlights
Typical log entries demonstrate task creation, successful cloning, addition failures due to duplicate names, and termination triggered by "+", "-", or "*" inputs. The framework requires unique task names; cloning logic should ensure uniqueness (e.g., by appending a random suffix).
INFO -> main task task A added successfully
INFO -> main task task B added successfully
INFO -> FT-1 task A Task is running!
INFO -> FT-2 task B Task is running!
+ // user input
INFO -> Receiver input: +
INFO -> Receiver task task A Clone added successfully
INFO -> FT-3 task A Clone Task is running!
- // user input
INFO -> Receiver input: -
INFO -> Receiver task task B Clone terminated
*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.
