不正确的延迟初始化

Findbug告诉我,我使用不正确的延迟初始化。

public static Object getInstance() { if (instance != null) { return instance; } instance = new Object(); return instance; } 

我在这里没有看到任何错误。 是findbug的错误行为,还是我错过了什么?

Findbug引用了潜在的线程问题。 在multithreading的环境中,你的单例可能会被当前代码多次创build。

这里有很多的阅读,但这将有助于解释。

这里的比赛条件是if check 。 在第一次调用时,一个线程将进入if check ,并将创build实例并将其分配给“实例”。 但是if check和实例创build/分配之间有另一个线程变成活动的可能性。 这个线程也可以通过if check因为任务还没有发生。 因此,会创build两个(或更多,如果有更多的线程进入)实例,并且您的线程将引用不同的对象。

你的代码比需要的稍微复杂一点,这可能是为什么它是混乱的。

编辑:这绝对是其他人发布的线程问题,但认为我会发布双锁检查实现在这里参考下面:

 private static final Object lock = new Object(); private static volatile Object instance; // must be declared volatile public static Object getInstance() { if (instance == null) { // avoid sync penalty if we can synchronized (lock) { // declare a private static Object to use for mutex if (instance == null) { // have to do this inside the sync instance = new Object(); } } } return instance; } 

注意 :JohnKlehm的双重locking解决scheme更好。 由于历史原因,在这里留下这个答案。

实际上应该是

 public synchronized static Object getInstance() { if (instance == null) { instance = new Object(); } return instance; } 

你需要在实例化过程中加一个锁来使其正确

LI:静态字段的不正确延迟初始化(LI_LAZY_INIT_STATIC)

此方法包含非易失性静态字段的非同步惰性初始化。 由于编译器或处理器可能会对指令进行重新sorting,因此如果可以由多个线程调用该方法,线程将不能保证看到完全初始化的对象。 您可以使该字段具有挥发性,以纠正问题。 有关更多信息,请参阅Java Memory Model网站。

感谢John Klehm发布的样本

也可以直接尝试在同步块中分配对象实例

 synchronized (MyCurrentClass.myLock=new Object()) 

 private static volatile Object myLock = new Object(); public static Object getInstance() { if (instance == null) { // avoid sync penalty if we can synchronized (MyCurrentClass.myLock**=new Object()**) { // declare a private static Object to use for mutex if (instance == null) { // have to do this inside the sync instance = new Object(); } } } return instance; } 

你错过了multithreading的问题,

 private static Object instance; public static synchronized Object getInstance() { return (instance != null ? instance : (instance = new Object())); } 

你的静态对象不同步。 而且你的方法不是懒惰的初始化。 通常你要做的是保持一个对象的Map,然后根据需要初始化所需的对象。 所以你不要在开始的时候初始化它们,而应该在需要的时候调用它们。

由于1.5:实例应该是volatile的,并且yould集成了一个tmpvariables来避免使用已经创build的实例,但是它的初始化还没有完成。

 private static volatile Object myLock = new Object(); private static volatile Object instance; public static Object getInstance() { if (instance == null) { Object tmpObj; synchronized (myLock) { tmpObj = instance; if (tmpObj == null) { tmpObj = new Object(); } } instance = tmpObj; } return instance; }