Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

C#中volatile的理解 #1

Open
bininc opened this issue Apr 16, 2018 · 0 comments
Open

C#中volatile的理解 #1

bininc opened this issue Apr 16, 2018 · 0 comments

Comments

@bininc
Copy link
Owner

bininc commented Apr 16, 2018

当进行多线程开发的时候,变量值的唯一性是不可避免需要讨论的问题了。
恐怕比较一下volatile和synchronized的不同是最容易解释清楚的。
volatile是变量修饰符,而synchronized则作用于一段代码或方法;看如下三句get代码:

  1. int i1; int geti1() {return i1;}
  2. volatile int i2; int geti2() {return i2;}
  3. int i3; synchronized int geti3() {return i3;}

geti1()方法得到存储在当前线程中i1的数值。多个线程有多个i1变量拷贝,而且这些i1之间可以互不相同。
换句话说,另一个线程可能已经改变了它线程内的i1值,而这个值可以和当前线程中的i1值不相同。
事实上,Java有个思想叫“主”内存区域,这里存放了变量目前的“准确值”。每个线程可以有它自己的变量拷贝,而这个变量拷贝值可以和“主”内存区域里存放的不同。
因此实际上存在一种可能:“主”内存区域里的i1值是0,线程1里的i1值 是1,线程2里的i1值是2
此时线程1和线程2都改变了它们各自的i1值,而且这个改变还没来得及传递给“主”内存区域或其他线程。

geti2()方法得到的是“主”内存区域的i2数值。用volatile修饰后的变量不允许有不同于“主”内存区域的变量拷贝。
换句话说,一个变量经volatile修饰后在所有线程中必须是同步的;任何线程中改变了它的值,所有其他线程立即获取到了相同的值。
理所当然,volatile修饰的变量存取时比一般变量消耗的资源要多一点,因为线程有它自己的变量拷贝更为高效。

geti3()方法是把方法整体放到“主”内存区域执行。既然已经有volatile了为啥还要有synchronized呢?
它们之间有两点不同:首先,synchronized获得并释放监视器——如果两个线程使用了同一个对象锁(lock),监视器能强制保证代码块同时只被一个线程所执行——这是众所周知的事实。但是,synchronized也同步内存:事实上,synchronized在“ 主”内存区域同步整个线程的内存。
因此,执行geti3()方法做了如下几步:

  1. 线程请求获得监视this对象的对象锁(假设未被锁,否则线程等待直到锁释放)
  2. 线程内存的数据被消除,从“主”内存区域中读入(Java虚拟机能优化此步。。。[后面的不知道怎么表达,汗])
  3. 代码块被执行
  4. 对于变量的任何改变现在可以安全地写到“主”内存区域中(不过geti3()方法不会改变变量值)
  5. 线程释放监视this对象的对象锁

因此volatile只是在线程内存和“主”内存间同步某个变量的值,而synchronized通过锁定和解锁某个监视器同步所有变量的值。显然synchronized要比volatile消耗更多资源。

更通俗的解释:

Volatile 字面的意思时易变的,不稳定的。在C#中也差不多可以这样理解。

编译器在优化代码时,可能会把经常用到的代码存在Cache里面,然后下一次调用就直接读取Cache而不是内存,这样就大大提高了效率。但是问题也随之而来了。

在多线程程序中,如果把一个变量放入Cache后,又有其他线程改变了变量的值,那么本线程是无法知道这个变化的。它可能会直接读Cache里的数据。但是很不幸,Cache里的数据已经过期了,读出来的是不合时宜的脏数据。这时就会出现bug。

用Volatile声明变量可以解决这个问题。用Volatile声明的变量就相当于告诉编译器,我不要把这个变量写Cache,因为这个变量是可能发生改变的。

原文引自:http://www.cnblogs.com/gjhjoy/p/3556709.html 对原作者描述根据自己的理解稍有修改

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant