Implementing Fixed‑Count and Fixed‑Time Load Tests with a Custom ThreadBase in Java
This article explains how to extend an existing Java performance‑testing framework by adding a fixed‑time mode alongside the traditional fixed‑count mode, detailing the design of ThreadBase, ThreadLimitTimes, ThreadLimitTime classes, demo implementations for HTTP requests, and key design considerations for low‑overhead timing and early termination.
ThreadBase
package com.fun.base.constaint;
import com.fun.frame.SourceCode;
import java.util.concurrent.CountDownLatch;
/**
* Multi‑thread task base class, can be used independently.
*/
public abstract class ThreadBase<T> extends SourceCode implements Runnable {
/** CountDownLatch set by the concurrent runner */
CountDownLatch countDownLatch;
/** Resource to be accessed by the task */
public T t;
protected ThreadBase() { super(); }
/** Groovy cannot access <code>t</code> directly, so this helper is provided */
public String getT() { return t.toString(); }
/** Hook executed before the test method */
protected abstract void before();
/** The method that performs the actual test */
protected abstract void doing() throws Exception;
/** Hook executed after the test method */
protected abstract void after();
public void setCountDownLatch(CountDownLatch countDownLatch) {
this.countDownLatch = countDownLatch;
}
}Fixed‑Count Mode – ThreadLimitTimes
This subclass limits the number of executions per thread. It records the duration of each execution and aggregates them in Concurrent.allTimes. The class also logs total execution count and elapsed time.
package com.fun.base.constaint;
import com.fun.frame.excute.Concurrent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
import static com.fun.utils.Time.getTimeStamp;
/**
* Multi‑thread class with request‑time limit, restricting the number of executions per thread.
*/
public abstract class ThreadLimitTimes<T> extends ThreadBase {
private static final Logger logger = LoggerFactory.getLogger(ThreadLimitTimes.class);
/** Number of times the task should be executed */
public int times;
public T t;
public ThreadLimitTimes(T t, int times) { this(times); this.t = t; }
public ThreadLimitTimes(int times) { this(); this.times = times; }
protected ThreadLimitTimes() { super(); }
@Override
public void run() {
try {
before();
List<Long> t = new ArrayList<>();
long ss = getTimeStamp();
for (int i = 0; i < times; i++) {
long s = getTimeStamp();
doing();
long e = getTimeStamp();
t.add(e - s);
}
long ee = getTimeStamp();
logger.info("执行次数:{},总耗时:{}", times, ee - ss);
Concurrent.allTimes.addAll(t);
} catch (Exception e) {
logger.warn("执行任务失败!", e);
} finally {
if (countDownLatch != null) countDownLatch.countDown();
after();
}
}
protected abstract void before();
protected abstract void doing() throws Exception;
protected abstract void after();
}Fixed‑Time Mode – ThreadLimitTime
This subclass runs until a specified time limit (in seconds) is reached or a static key flag is set, allowing early termination. It similarly records per‑iteration durations.
package com.fun.base.constaint;
import com.fun.frame.excute.Concurrent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
import static com.fun.utils.Time.getTimeStamp;
/**
* Multi‑thread class with request‑time limit, restricting execution time per thread.
*/
public abstract class ThreadLimitTime<T> extends ThreadBase {
private static boolean key = false; // global stop switch
private static final Logger logger = LoggerFactory.getLogger(ThreadLimitTime.class);
/** Execution time limit in seconds (stored internally as milliseconds) */
public int time;
public T t;
public ThreadLimitTime(T t, int time) { this(time); this.t = t; }
public ThreadLimitTime(int time) { this(); this.time = time * 1000; }
protected ThreadLimitTime() { super(); }
/** Call to request early termination of all threads */
public static void stopAllThread() { key = true; }
@Override
public void run() {
try {
before();
List<Long> t = new ArrayList<>();
long ss = getTimeStamp();
while (true) {
long s = getTimeStamp();
doing();
long e = getTimeStamp();
t.add(e - s);
if ((e - ss) > time || key) break;
}
long ee = getTimeStamp();
logger.info("执行时间:{} s,总耗时:{}", time / 1000, ee - ss);
Concurrent.allTimes.addAll(t);
} catch (Exception e) {
logger.warn("执行任务失败!", e);
} finally {
if (countDownLatch != null) countDownLatch.countDown();
after();
}
}
protected abstract void before();
protected abstract void doing() throws Exception;
protected abstract void after();
}HTTP Request Demo Implementations
Two concrete classes illustrate how to use the abstract bases for real HTTP calls.
/** http request multithread class */
public class RequestThreadTimes extends ThreadLimitTimes {
static Logger logger = LoggerFactory.getLogger(RequestThreadTimes.class);
public HttpRequestBase request;
public RequestThreadTimes(HttpRequestBase request, int times) {
this.request = request;
this.times = times;
}
@Override public void before() { GCThread.starts(); }
@Override protected void doing() throws Exception { getResponse(request); }
@Override public void after() { GCThread.stop(); }
/** Execute a request without logging; suitable for repeated single‑request scenarios */
void getResponse(HttpRequestBase request) throws IOException {
CloseableHttpResponse response = ClientManage.httpsClient.execute(request);
String content = FanLibrary.getContent(response);
if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK)
logger.warn("响应状态码:{},响应内容:{}", content, response.getStatusLine());
response.close();
}
} /** http request multithread class */
public class RequestThreadTime extends ThreadLimitTime {
static Logger logger = LoggerFactory.getLogger(RequestThreadTime.class);
public HttpRequestBase request;
public RequestThreadTime(HttpRequestBase request, int time) {
this.request = request;
this.time = time;
}
@Override public void before() { GCThread.starts(); }
@Override protected void doing() throws Exception { getResponse(request); }
@Override public void after() { GCThread.stop(); }
void getResponse(HttpRequestBase request) throws IOException {
CloseableHttpResponse response = ClientManage.httpsClient.execute(request);
String content = FanLibrary.getContent(response);
if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK)
logger.warn("响应状态码:{},响应内容:{}", content, response.getStatusLine());
response.close();
}
}Usage Example
The following main method demonstrates how to instantiate a fixed‑time thread, provide the three lifecycle callbacks, and start the test with a concurrency runner.
package com.fun;
import com.fun.base.constaint.ThreadLimitTime;
import com.fun.frame.SourceCode;
import com.fun.frame.excute.Concurrent;
public class AR extends SourceCode {
public static void main(String[] args) {
ThreadLimitTime<Object> threadLimitTime = new ThreadLimitTime<Object>(10) {
@Override protected void before() { }
@Override protected void doing() throws Exception { AR.test(); }
@Override protected void after() { }
};
new Concurrent(threadLimitTime, 5).start();
FanLibrary.testOver();
}
public static void test() {
synchronized (AR.class) {
sleep(100);
output("fun");
}
}
}Design Notes
The static key flag enables an emergency stop without spawning a separate timer thread; timestamp comparisons are cheap and avoid extra overhead.
Both modes rely on a shared Concurrent helper that aggregates per‑iteration timings for later analysis.
Groovy compatibility is addressed by exposing getT() because Groovy scripts cannot directly access generic fields.
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.
