为什么要使用双重锁定?

Meh*_*dad 5 c# java synchronization compare-and-swap double-checked-locking

我继续运行使用双重检查锁定的代码,我仍然对它为什么会被使用感到困惑.

我最初不知道双重检查锁定是否被打破,当我学会它时,它为我放大了这个问题:为什么人们首先使用它?是不是比较和交换更好?

if (field == null)
    Interlocked.CompareExchange(ref field, newValue, null);
return field;
Run Code Online (Sandbox Code Playgroud)

(我的问题同时适用于C#和Java,尽管上面的代码是针对C#的.)

与原子操作相比,双重检查锁定是否具有某种固有优势?

Eri*_*ert 12

与原子操作相比,双重检查锁定是否具有某种固有优势?

(这个答案只涉及C#;我不知道Java的内存模型是什么样的.)

主要区别在于潜在的种族.如果你有:

if (f == null)
    CompareExchange(ref f, FetchNewValue(), null)
Run Code Online (Sandbox Code Playgroud)

然后可以在不同的线程上任意多次调用FetchNewValue().其中一个主题赢得了比赛.如果FetchNewValue()非常昂贵,并且您希望确保仅调用一次,那么:

if (f == null)
    lock(whatever)
        if (f == null)
            f = FetchNewValue();
Run Code Online (Sandbox Code Playgroud)

保证FetchNewValue只被调用一次.

如果我个人想要进行低锁懒惰初始化,那么我按照你的建议行事:我使用互锁操作并忍受罕见的竞争条件,其中两个线程都运行初始化器并且只有一个获胜.如果这是不可接受的,那么我使用锁.


Mat*_*att 0

好吧,我想到的唯一优点是性能(的错觉):以非线程安全的方式检查,然后执行一些锁定操作来检查变量,这可能会很昂贵。然而,由于双重检查锁定的破坏方式阻止了非线程安全检查的任何明确结论,而且无论如何,它总是带有过早优化的味道,我会声称不,没有优势 - 这是一个过时的预- Java 时代的习惯用法 - 但希望得到纠正。

编辑:要清楚(呃),我相信双重检查锁定是一种习惯用法,它是作为每次锁定和检查的性能增强而演变的,并且粗略地说,与非封装的比较和交换接近相同的东西。不过,我个人也喜欢封装同步代码段,因此我认为调用另一个操作来完成脏工作更好。