Atomic Operation Pitfalls in Java Performance Testing and How to Fix Them
This article describes a Java performance‑testing script that uses concurrent user initialization, explains how overlooking atomicity in thread‑safe classes caused some users to miss initialization, and presents three practical solutions such as improving stop logic, tracking completed users, and using concurrent collections.
During performance testing with Java, I have encountered many self‑inflicted pitfalls. After weathering many storms, I considered myself a seasoned concurrent programming driver, only to fall into the same trap again recently.
Maintain operation atomicity!!!
Maintain operation atomicity!!!
Maintain operation atomicity!!!
Important things are written three times.
The task is to write a script that initializes all users (including HTTP requests). To speed up, concurrency is used (hidden requirement: each user must be initialized, but repetitions are allowed).
To achieve dynamic QPS adjustment, a dynamic QPS implementation is chosen.
Below is the pseudo‑code implementation:
package com.funtest.temp
class AtomicTest {
static List
users
static class User {
String token
int uid
void init() {
//用户初始化
}
}
}The initial idea is to use a previously prepared sequential (pseudo‑random) method:
/**
* 随机选择某个对象
*
* @param list
* @param index 自增索引
* @param
* @return
*/
public static
F random(List
list, AtomicInteger index) {
if (list == null || list.isEmpty()) ParamException.fail("数组不能为空!");
return list.get(index.getAndIncrement() % list.size());
}Then, using a thread‑safe random method, each request uses a different user. By inspecting the actual increment of the index , we determine whether all users have been traversed.
static void main(String[] args) {
AtomicInteger index = new AtomicInteger()
def test = {
SourceCode.random(users, index).init()
if (index.get() > users.size()) FunQpsConcurrent.stop()
}
new FunQpsConcurrent(test, "原子操作BUG演示").start()
}But the bug appears: when testing, the last few users in static List<User> users never complete initialization. After simple investigation, I found my own mistake.
Using a thread‑safe class does not guarantee safety
Because thread‑safe classes are safe for atomic operations, I will share the test{} again.
def test = {
1. def random = SourceCode.random(users, index)
2. random.init()
3. if (index.get() > users.size()) FunQpsConcurrent.stop()
}When line 1 finishes, the index has already incremented. However, other threads reaching line 3 read the already‑incremented index value and may trigger the program to exit prematurely.
Three handling methods:
Optimize the stop() method; once the index reaches the threshold, the execution is forcibly terminated. In reality, the program should wait until the test{} block finishes completely before exiting.
Add an extra java.util.concurrent.atomic.AtomicInteger object to count the number of users that have completed execution, instead of relying on the random selection count.
Use a thread‑safe collection such as java.util.concurrent.LinkedBlockingQueue and exit only when poll() returns null.
Today's sharing ends here; actually Java performance testing is simple, and with more practice and pitfalls, it becomes easy to master.
FunTester原创专题推荐~ 900原创合集 2021年原创合集 2022年原创合集 接口功能测试专题 性能测试专题 Groovy专题 Java、Groovy、Go、Python 单测&白盒 FunTester社群风采 测试理论鸡汤 FunTester视频专题 案例分享:方案、BUG、爬虫 UI自动化专题 测试工具专题 -- By FunTester
FunTester
10k followers, 1k articles | completely useless
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.