2012-06-19 75 views
3

我在我们的一个项目中遇到了一个奇怪的问题。我们使用JUnit来运行我们的单元测试,并且前一段时间,我们开始并行运行pur测试来加速执行。大多数时候,一切都很好,但有时我们几乎所有的测试都失败了。在下一次运行中,他们都会再次通过,而不会更改任何代码。多线程和静态块

这些错误似乎表明某些静态实例未正确初始化或在多线程情况下初始化完成之前使用。 (我不能调试这个,因为这个问题从来没有一次露面在调试的时候 - >Heisenbug

对不起,因为它试图重现消失的时候,我不能提供显示的bug最小工作示例。

具体的问题是:当声明一个像下面这样的变量时,当另一个线程调用foo()或bar()时,a或b的初始化是否还没有完成?我认为静态块将保证在任何方法被调用之前执行。或者会有类加载器问题?或者JRE中的已知错误(我们目前停留在1.6.0_21,我们的IT部门尚未提供更新的版本)?

class C { 
    private static final A a; 
    private static final B b; 

    static { 
     a = new A(...); 
     b = new B(...); 
    } 

    public static void foo() { 
     useA(); 
    } 

    public static void bar() { 
     useB(); 
    } 

} 

我确定它不是硬件相关的,因为它显示在不同制造商的不同机器上。测试正在使用服务器虚拟机。

谢谢

阿克塞尔

+0

新A()或新B()是否开始新线程?你是否试图找出问题并创建一个能够再现问题的[SSCCE](http://sscce.org)? – assylias

+0

因为我知道,使用整个类的静态字段和方法是一个不好的做法,尤其是使用多线程。为什么不创建非静态类并将其存储在静态字段中? – alaster

+0

这就是在这里完成的。 a和b是提供预先计算值的类的实例。这两个类都是不可变的,并且有方法返回这些预先计算的值。计算是在相应的类构造函数中完成的。在访问时,只有检查参数,计算索引并返回包含以前计算值的数组元素。 – Axel

回答

0

这是最有可能是由于并发性问题,特别是如果你打电话static东西。尝试同步你的线程,就像这样:

class C { 
    private static final A a; 
    private static final B b; 

    static { 
     a = new A(...); 
     b = new B(...); 
    } 

    public synchronized static void foo() { 
     useA(); 
    } 

    public synchronized static void bar() { 
     useB(); 
    } 

} 
+0

A类和B类是不可变的,且经过彻底测试的线程安全。应该没有必要在这里使用同步。在我们的测试中,foo()和bar()被调用了数百万次,并且总是失败或永远不会失败,所以我认为它必须与初始化有关。 – Axel

+0

静态字段初始化**一次**(它们与类相关而不与实例相关)。你没有详细说明如何从C中调用A和B实例,因此对于我来说它们在你的例子中是多余的。 – m0skit0

+0

是的,初始化一次。而且在初始化过程中似乎出现了问题。 – Axel

0

可能有问题,如果AB创建线程或以其他方式在不同的线程中执行代码。无论如何,你真的希望静态是不可变的。

如果有循环依赖关系,理论上可以看到一个部分初始化的类,但这是不太可能的。