我应该同时使用 lock 和 volatile 吗?

Posted

技术标签:

【中文标题】我应该同时使用 lock 和 volatile 吗?【英文标题】:Should I be using both lock and volatile? 【发布时间】:2014-08-12 21:25:33 【问题描述】:

我对 volatile 的理解是它确保始终从内存中读取值,因此据我所知,在以下示例中,myObject 变量需要是 volatile 以避免 NullPointerException 被提出:

private final Object lock = new Object();
private MyObject myObject = null;

//...

synchronized (lock) 
    if (myObject == null) 
        myObject = new MyObject();
    

    myObject.blah();

    // some other stuff that I want synchronized

myObject 只在同步块中被触及。 lock 仅用于同步该块。

对吗?

稍微改写一下,我的问题是……想象一下有两个线程正在访问该代码。第一个线程锁定并设置 myObject,调用 .blah() 和同步块中的任何其他代码并退出同步块。这允许线程二进入同步块。如果不将 myObject 设置为 volatile,它是否仍有可能将 myObject == null 评估为 true

【问题讨论】:

myObject 易变吗?另外,Object lock constant 是否适用于您班级的所有实例? @LuiggiMendoza - 见编辑。 还有其他代码可以将myObject设置为null吗? @SotiriosDelimanolis - 没想到这段代码是自包含的。 我认为可能有机会,我在使用synchronized 块时没有那么愉快的经历,其中发生了意外的结果并且同步不起作用,尤其是在进程开始时。最好使用ReentrantLock。而volatile在这种情况下与同步无关,所以不需要。 【参考方案1】:

synchronized 块将确保其他线程可以看到对内存的更新。无需使 myObject 易失。

来自Intrinsic Locks and Synchronization:

当一个线程释放一个内在锁时,一个happens-before 该行动与任何后续行动之间建立了关系 获取同一个锁。

【讨论】:

我一直在做一些研究,因为我想知道为什么我认为你需要 volatile 关键字。我相信这与 DCL 有关,这是:cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html。现在我说得对吗,因为我没有在同步循环之外检查 null,我不需要检查?【参考方案2】:

我认为这里不需要 volatile,因为进入同步块的每个线程都在检查 myObject 引用,所以当第一个线程进入块时应该实例化 myObject,其他线程通过检查 myObject 不为空来保护。对我来说看起来不错。

编辑:我希望只有这一块您想使用 myObect 引用,并且您不要在同步块之前或之后更改此引用。

【讨论】:

但是该值是否不可能存在于某处的缓存中并因此导致 NPE? 没有。没有这种可能性。 在您粘贴的代码中,每个线程(但当时只有一个)将检查 myObject 值,然后运行 ​​blah 方法,因此其他所有线程都会等待。但是你不能在你设置 myObject=null 的地方实现第二个块,因为那样会像你说的那样发生。如果你想将 null 设置为 myObject,你需要在一个块中完成。

以上是关于我应该同时使用 lock 和 volatile 吗?的主要内容,如果未能解决你的问题,请参考以下文章

映射GPU内存时应该使用volatile吗?

在C语言的多线程编程中一般volatile应该用在啥地方?

使用volatile修饰int型变量i,多个线程同时进行i++操作,这样可以实现线程安全吗

volatile与lock前缀指令

在 Dart 和 Pub 中,我应该将 pubspec.lock 添加到我的 .gitignore 吗?

在 Dart 和 Pub 中,我应该将 pubspec.lock 添加到我的 .gitignore 吗?