我有一个看似很奇怪的问题,可能是因为我缺乏一些特定的知识。
我有一个简单的TestCon类和一个测试类TestTest:
import java.util.*;
import java.util.concurrent.*;
public class TestCon {
private volatile static TestCon def = null;
private volatile static List<TestCon> list = Collections.synchronizedList(new ArrayList<TestCon>());
public static synchronized TestCon getInstance() {
if (def == null) {
def = new TestCon();
list.add(def);
}
return def;
}
private synchronized static TestCon addtolist(){
return new TestCon();
}
public static synchronized int getSize() {
return list.size();
}
public synchronized void destroy() {
def = null;
list.clear();
}
}
import org.testng.annotations.Test;
import org.testng.*;
public class TestTest {
@Test(threadPoolSize=50, invocationCount=15000)
public void test_getInstance() {
TestCon tst=TestCon.getInstance();
Assert.assertNotNull(tst);
Assert.assertEquals(TestCon.getSize(), 1); //line 1
tst.destroy(); //line 2
}
}
所以我的问题是为什么测试偶尔会在//line 1
失败(列表大小为0但预期为1)。如果我在测试方法中添加同步 - 一切都很好。如果我再次评论第2行测试成功。
答案 0 :(得分:3)
虽然您的TestCon
类没有呈现明确的竞争条件,但您的测试包含一个。虽然getInstance
,getSize
和destroy
方法是原子的,但这些方法的任何组合都不是。
查看您的代码:
@Test(threadPoolSize = 50, invocationCount = 15000)
public void test_getInstance() {
TestCon tst = TestCon.getInstance();
Assert.assertNotNull(tst); // (1)
Assert.assertEquals(TestCon.getSize(), 1); // (2)
tst.destroy(); // (3)
}
假设您有两个线程最终在第(1)行结束。然后,线程1成功地移动到第(3)行。这意味着,列表现在是空的。在此线程之后,线程2移动并检查列表大小(2)并找到列表为空:测试失败。
答案 1 :(得分:2)
synchronized
只是保护它后面的代码块(方法体或方法中的语句块)。
这意味着锁定同一实体的其他synchronized
块(因为您使用synchronized
方法,因此您的案例中的类)无法同时执行但是它没有&#34;#34;顺序&#34;个别街区。
你看到的是:
TestCon tst=TestCon.getInstance();
tst.destroy(); //line 2
Assert.assertEquals(TestCon.getSize(), 1); //line 1
这是因为您的代码只会创建一个所有线程都使用的TestCon
。
相反,您需要确保每个线程都有自己的TestCon
。查看ThreadLocal
以获取每个线程具有不同值的字段。