如何从多个线程测试类的线程安全性?

时间:2015-12-16 18:50:19

标签: java multithreading thread-safety

我正在开发线程安全的单例类,我知道下面的代码不是线程安全的,因为我正在进行双重检查锁定错误。

protected static TestSingleton instance;

private TestSingleton() {
    // some code
}

public static synchronized void setup() {
    if (instance == null) {
        TestSingleton holder = new TestSingleton();
        instance = holder;
    }
}

public static TestSingleton getInstance() {
    if (instance == null) {
        setup();
    }
    return instance;
}

但我正在尝试对此进行测试,以便我可以通过编程方式证明这不是线程安全的类。这是我的学习经历。

如何以编程方式测试并证明它不是线程安全的?

4 个答案:

答案 0 :(得分:3)

归结为:您的程序有一定数量的线程,在某些处理器上运行,但您的计算机只有一个主内存。

每个线程将以某种顺序读取和写入不同的内存位置,这与其他线程正在做的有些独立;所有这些读写操作都必须序列化,因为它们会出现在内存中。也就是说,内存系统必须逐个执行这些读写操作。

对于任何给定的多线程程序执行,可以有多种不同的方式可以序列化不同线程的操作。从字面上看,太多不值得了。如果即使其中一个可能的序列化导致程序产生错误的输出,那么您的程序也不是线程安全的。

那么你怎么能测试它们呢?

你不能。它们太多了,并且可能在一台计算机上频繁发生的序列化(例如,您的客户的关键任务服务器)可能从未发生在其他计算机上(例如,您的测试工具)。 )

确保线程安全的唯一方法是使用经过数学​​验证的算法来同步线程的操作,以便不会发生错误的交错。

答案 1 :(得分:2)

我已经看到为什么我的以下回答不正确。请参阅相关评论主题以了解原因。

我不认为可以证明你的程序在当前的版本中不是线程安全的,因为这些方法中的逻辑是安全的'够了。

<击>

这是因为即使尝试同时调用getInstance()内部的setup()调用,setup()内部的逻辑也将同步发生,因为该方法被声明为synchronized。

想象一下,你有两个线程,A和B,竞争访问setup()。 A首先在实例变量中设置对TestSingleton对象的引用。当A在setup()方法内部时,B尝试调用它并被阻止。 A完成,B进入方法。由于实例现在不为null,因此,B中不会调用setup()中的逻辑。因此,您将永远不会在setup()中看到条件块内部逻辑的多次调用。

答案 2 :(得分:1)

简短回答:你做不到。

无论您在测试环境中运行此代码多少次,其实际工作的概率都非零。 问题是,你不能真正估计这个概率,因此无法找到他们显示正确结果的概率所需的一些测试运行接近1。

您实际上可以推断某个程序可能的执行方式。找到多线程程序中断的方法通常比证明它永远不会破坏更容易。

在这种情况下,存在以下问题:在线程1中instance执行期间setupinstance执行读取以从线程2中的getInstance执行返回值,也不执行从instance执行的读取以检查null在线程2中的setup执行中。这意味着来自不同线程的两个调用可能产生不同的结果,即实现被破坏,我们甚至没有开始考虑代码重新排序。

通过声明instance volatile 来建立这样的关系会解决它。

您可以通过Alexey Shipilev在this excellent paper about Java Memory Model中了解有关顺序一致性,程序顺序和先发生关系的更多信息。这对你很有帮助。

不幸的是,目前无法实现所述推理的自动化。

答案 3 :(得分:0)

通过您描述的测试,您最有可能查找的内容是两个线程从TestSingleton方法获取getInstance()的不同实例,或获取null的线程返回该方法的值,或者在完全初始化之前返回实例的线程。

问题在于你无法确定其中任何一种发生的可能性,实际上,它们可能非常不太可能。您可能会运行大量的测试迭代,而不会看到任何这些失败行为。如果您看到其中一个,那么您将证明该程序不是线程安全的,但是没有看到它们中没有任何证据。

线程安全是一个程序属性,您必须通过分析程序来证明。在有限的时间内进行测试无法证明这一点。