我应该同时使用 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 吗?的主要内容,如果未能解决你的问题,请参考以下文章
使用volatile修饰int型变量i,多个线程同时进行i++操作,这样可以实现线程安全吗