在做性能测试的过程中,我写了两个虚拟类​​ThreadLimitTimeCount​​​和​​ThreadLimitTimesCount​​做框架,通过对线程的标记来完成超时请求的记录。旧方法如下:

@Override
protected void after() {
requestMark.addAll(marks);
marks = new ArrayList<>();
GCThread.stop();
synchronized (this.getClass()) {
if (countDownLatch.getCount() == 0 && requestMark.size() != 0) {
Save.saveStringList(requestMark, MARK_Path.replace(LONG_Path, EMPTY) + Time.getDate().replace(SPACE_1, CONNECTOR));
requestMark = new Vector<>();
}
}
}复制代码

其中我用了​​synchronized​​关键字同步,但是在匿名类的单元测试中出现一个BUG,匿名类中没有实现​​clone()​​方法,也不能直接使用深拷贝方法,导致无法直接复制对象,所以我创建了多个功能相同的匿名线程类。问题来了,在代码执行过程中,偶然会出现记录​​markrequest​​的文档中出现空内容的形式。

我查询了一些资料,感觉问题出现在​​synchronized (this.getClass())​​这个问题了,因为我打印​​this.getClass()​​给我的是当前测试类的类名,感觉原因就是匿名类的问题,匿名类相当于多个实现类,​​synchronized (this.getClass())​​无法保证多各类对象同时访问这个方法的线程安全。最终,我选择了另外一种方式,就是单独写一个线程安全的​​save()​​方法,这样就可以保证所有访问保存方法的线程的安全,将清空记录列表的功能也放在了这个线程安全的方法里了。

/**
* 同步save数据,用于匿名类多线程保存测试数据
*
* @param data
* @param name
*/
public static void saveStringListSync(Collection<String> data, String name) {
synchronized (Save.class) {
if (data.isEmpty()) return;
saveStringList(data, name);
}
}复制代码

原来虚拟类的方法就变成了如下的样子:

if (countDownLatch.getCount() == 0 && requestMark.size() != 0) {
Save.saveStringListSync(requestMark, MARK_Path.replace(LONG_Path, EMPTY) + Time.getDate().replace(SPACE_1, CONNECTOR));
}