【发布时间】:2014-10-30 08:04:52
【问题描述】:
我有一个单例类要测试:
public class Singleton {
private static Singleton instance;
private List<String> list;
private Singleton() {
}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
instance = new Singleton();
}
}
return instance;
}
public boolean methodOne() {
if (list == null) {
list = new ArrayList<String>();
list = SomeClass.fillListOne();
}
return SomeClass.verifyList(list);
}
public boolean methodTwo() {
if (list == null) {
list = new ArrayList<String>();
list = SomeClass.fillListTwo();
}
return SomeClass.verifyList(list);
}
}
使用以下测试类:
@RunWith(JMockit.class)
public class SingletonTest {
@Test
public void testOne(final @Mocked SomeClass someClass) {
Singleton.getInstance().methodOne();
new Verifications() {
{
SomeClass.fillListOne();
}
};
}
@Test
public void testTwo(final @Mocked SomeClass someClass) {
Singleton.getInstance().methodTwo();
new Verifications() {
{
SomeClass.fillListTwo();
}
};
}
}
如果我只执行“testOne”或只执行“testTwo”,则测试通过。如果我执行所有测试,它只会通过第一个执行的方法。如何将“list”属性设置为 null,例如在 @Before 方法中?如何在没有设置器的情况下使用单例或私有成员的解封装?
【问题讨论】:
-
仅供参考:getInstance 方法非常不安全。您不能在空检查内部进行同步并期望它是安全的。线程 A 进来并检查实例是否为空,它不是,所以它继续。上下文切换,线程 B 进来检查实例是否为空,它仍然不是,所以它继续。线程B获取锁,初始化一些Object,分配给instance(保证按照synchronized的要求分配和初始化),释放锁。上下文切换,线程 A 现在获取锁并执行相同操作,覆盖 ThreadB 的实例。
-
Java 并发开发新手的推荐读物:cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html(你犯了第一个错误,这里详述)
-
澄清:“(保证按照synchronized的要求进行赋值和初始化)”,意思是在释放锁的时候,保证对象被赋值和初始化。根据 JMM,同步块内部发生的顺序是不确定的。为了进一步澄清,您的示例确实成为该问题的牺牲品,但也成为我最初示例的牺牲品(这与 JMM 无关,但知道 JMM 如何处理此问题将减轻您对该问题的“解决方案”,链接 I发布了为什么它不起作用的详细信息)。
-
感谢 searchengine27,但问题不在于单例和同步。正如我在较早的评论中所写,我无法编辑原始课程。
标签: java junit singleton private-members jmockit