5

Java 程序员都知道,为了使双重检查锁定能够正常工作,必须声明变量volatile,并且同步对象的初始化是不够的。

这种认识可能主要是因为volatile关键字的语义在 1.5 中被更改为包含“发生在之前”关系,至少部分是为了使双重检查锁定安全;据我了解,“之前发生”关系意味着写入 volatile 变量会导致线程中的所有缓存变量都写入主内存,并且在从 volatile 变量中读取之后,所有缓存的变量都被认为是陈旧的,并且必须是从主存储器重新读取,以便在写入 volatile 变量之前写入的所有内容都保证“发生在”以后从该变量读取之前。

Stack Overflow似乎认为,对于 C#,volatile双重检查锁定是不必要的(尽管注意到这可能特定于某些 CPU 或 Microsoft 的实现),同时还认为 Javasynchronized语句的语义与C# 的完全一样lock声明,这表明在 Java 中确定的相同问题也将存在于 C# 中,除非在两种语言之间的双重检查锁定的语义上存在其他一些主要差异。

那么......哪个是正确的?C# 中的双重检查锁定实际上比 Java 中的危险小吗?如果是这样,有什么不同的语言语义可以做到这一点?

如果没有,具体会出现什么问题volatile?C#中的语义是否volatile像 Java 一样建立了“先发生”关系,因此双重检查锁定在 C# 中与volatile自 1.5 以来在 Java 中一样安全?

4

1 回答 1

3

来自MSDN:“volatile 关键字表示一个字段可能会被同时执行的多个线程修改。声明为 volatile 的字段不受编译器优化的影响,假设由单个线程访问。这确保了最多-to-date 值始终存在于该字段中。”

因此, volatile 仅在您从多个线程更改变量的值时才有用。如果您 lock() 使用相同锁定对象访问共享资源的代码区域,则可以保证只有一个线程可以同时访问这些代码区域,那么除非您正在修改锁定对象(这是一个非常糟糕的主意),否则不需要 volatile .

于 2014-04-15T00:18:36.050 回答