Java 潜在并发问题的单元测试

Java 潜在并发问题的单元测试,java,multithreading,unit-testing,concurrency,thread-safety,Java,Multithreading,Unit Testing,Concurrency,Thread Safety,最近,我与我的一位同事(我非常尊敬他)就测试某些代码是否线程安全的理论可能性进行了一场小型竞赛 假设我们有一个来自第三方库的“黑盒”类FooUnknown,因此我们无法访问其原始源代码。此外,它可能在内部使用一些本机方法(如果这是一个问题的话) 我们是否可以编写这样一个单元测试来告诉我们这个类(例如,它的实例在多个线程之间共享)的使用是100%线程安全的 我的结论是,这是不可能的。对我来说,这是显而易见的和直截了当的:尽管人们可以编写一段代码,这将导致一些可能检测到的并发性问题。但缺少这样的结果

最近,我与我的一位同事(我非常尊敬他)就测试某些代码是否线程安全的理论可能性进行了一场小型竞赛

假设我们有一个来自第三方库的“黑盒”类
FooUnknown
,因此我们无法访问其原始源代码。此外,它可能在内部使用一些本机方法(如果这是一个问题的话)

我们是否可以编写这样一个单元测试来告诉我们这个类(例如,它的实例在多个线程之间共享)的使用是100%线程安全的

我的结论是,这是不可能的。对我来说,这是显而易见的和直截了当的:尽管人们可以编写一段代码,这将导致一些可能检测到的并发性问题。但缺少这样的结果并不能保证根本不存在并发性问题

我还认为,这个问题并不是太宽泛。为了确定起见,让我们假设我们有一个类
some.FooUnknown
,我们希望以以下方式使用它:

@ApplicationScoped
public class FooService {
    private some.FooUnkown foo = new some.FooUnknown();

    public void someStuff() {
        // ...
        String result = foo.doSomeStuff();
        // ...
    }
}

如何测试它以确保它是线程安全的,例如,我们不需要将它包装到
ThreadLocal
中?

您是正确的-作为一个黑匣子,您不可能说它是否是线程安全的。。即使您测试它两周并在一百万个虚拟机上投入所有精力,它现在可能是线程安全的,但在2018年将变得线程不安全(例如,通过内部检查日期并在2018-01-01故意死锁)

这是不可能的,因为有很多方法可以产生线程不安全。在本机代码中只需一个全局变量就足够了

忽略所有实际原因,这也是理论上很难实现一套完整的并发安全测试的原因:

假设有
n个
线程在同一数据结构上运行。然后,任何线程
i
在此数据结构上都有一个Si原子操作序列,其中每个序列可能具有不同的长度。现在,在理想的环境中,您需要确保通过这些操作跨越所有线程的每个可能的迭代序列都被测试覆盖。即使对于相对较小的操作序列和只有2个线程,这个数字增长也相当快


但现在困难的部分是将这些发现转化为一台真正的计算机。鉴于JVM的实现方式和java内存模型的自由,识别这样的原子操作本身就是一项复杂的任务。然后还有由操作系统控制的线程调度。因此,数据结构上发生的实际操作顺序通常是您无法控制的。

完全同意您的看法。但我想保持这个问题的开放性,以便给对方提供一个机会,让他们提供他们棘手的想法:-)对这个问题的投票也会引起人们越来越大的兴趣:)实际上,若我们将自己限制在一个特定ISA的线程安全上,这在理论上是可行的。只需对每个可能的输入执行每个可能的指令排列(两者都必须是有限的),并检查每个可能的输入的每个可能的指令序列的输出是否相同。不太实用,但保证能工作。(与您的示例相反的技巧是,“获取当前日期”将是一个输入,因此您必须检查每个可能值的输出)。@Voo这是有趣的一点,但您不能保证在测试期间没有重新排序的指令。你不能强迫它发生,所以你不能确定即使它发生了,你也没有问题:看它的说明也不完全是黑盒测试。。如果你能往里看,与在所有可能的输入上运行所有可能的排列相比,仅仅对其进行反向工程可能至少要快4倍。伙计,即使是停止问题也无法从算法上解决。“你在说什么?@AntonMalyshev不幸的是,你的问题太不具体了,我无法改进我的答案。我只是指出,你的答案中没有“理论性”内容。每个人加1,但对我来说,全局变量的想法似乎是最好的