今天在stackoverflow上看到一个关于Volatile, Interlock, Lock的问题,发现回答的特别好,所以就想到把它翻译一下, 希望给那些对它们有疑惑的人提供点帮助

:假设有一个类,它含有一个可以被多线程访问的public int counter 字段, 这个数字只会增加或减少。

当去增加这个字段的时候,应该采用下面哪个方案,为什么?

  • lock(this.locker) this.counter++;
  • Interlocked.Increment(ref this.counter);
  • Change the access modifier of counter to public volatile

最糟糕(实际上都不能起作用)

Change the access modifier of counter to public volatile

这种方式实际上根本不安全,关于volatile的重点是:运行在多个CPU上的多个线程会缓冲数据和重新排列执行的指令。

如果它是非volatile, 当CPU A增加了一个值, CPU B需要等一会才能看到增加的值,这可能导致问题的出现。

如果它是volatile,就能确保两个CPU能在相同的时间看到相同的值。但它并不能避免交叉的读写操作。

给一个变量增加值,实际上需要三步

1. 读, 2. 增加 3.写

假设线程A 读取 counter 的值为1,还没有准备增加,这时线程B也读取counter 的值为1, 然后两个线程都开始执行增加,写的操作。最终counter 的值为2. 这是不对的,两个线程都做了增加操作,正确的结果应该是3. 所以把它标志为volatile根本就是不安全的。

比较好

lock(this.locker) this.counter++;

这种方式是安全的(当然你要记得lock所有你想访问this.counter的地方)。它防止其它任何线程去执行被lock锁住的代码。并且它还能防止上面提到的多CPU指令排序的问题。但问题是,lock在性能上比较慢,并且如果你在其它的一些不相干的地方也用了lock,可能会导致阻塞你的其它线程。

最好

Interlocked.Increment(ref this.counter);

这个是安全的,也是非常高效的。它执行读,增加,写三个操作在一个原子中,不会在中间被打断。因为它不影响其它的代码,你也不需要记住其它地方的Lock. 并且它还非常快(正如MSDN说的,在现在的CPU上,它常常只是一个指令)。

但是我不完全确定它能否也能解决CPU的指令排序的问题,还是需要结合volatile和这个increment一起使用。

补充:到底volatile是擅长解决什么问题的?

既然volatile不能防止多线程的问题,那它能干啥用?举个很好的例子,假设你有两个线程,一个总是往一个变量中进行写操作,假设这个变量为queneLength, 另一个总是从这个变量中读取数据。如果queueLenght不是volatile的, 线程A可能读了5次,但是线程B可能看到的是延迟的数据,甚至可能看到错误的顺序的数据。一个方案就是用lock, 但是在这种情况系你也可以用volatile. 这样能确保线程B总能看到线程A写的最新数据,但是这个这个逻辑仅仅在你写的时候没有读,读的时候没有写时才有效。一旦多个线程都要做读-修改-写的操作,你需要用Interlocked或者用lock.

Volatile vs. Interlocked vs. lock的更多相关文章

  1. 多线程中的锁系统(二)-volatile、Interlocked、ReaderWriterLockSlim

    上章主要讲排他锁的直接使用方式.但实际当中全部都用锁又太浪费了,或者排他锁粒度太大了,本篇主要介绍下升级锁和原子操作. 阅读目录 volatile Interlocked ReaderWriterLo ...

  2. 多线程---再次认识volatile,Synchronize,lock

    在多线程中我们常用的保证共享变量的方法有很多,现在我们介绍其中的一种,volatile,也是效率最高的一种.    一 .volatile的意义:             为了确保共享变量能被正确和一 ...

  3. volatile和synchronized与lock的理解

    volatile 特征: a:可见性:一个线程修改了某个共享变量的值,其他线程能够立马得知这个修改. b:禁止特定的处理器重排序. volatile的内存语义: 1.当写一个volatile变量的时候 ...

  4. volatile 和 Interlocked

    class Volatile_Test3 { ; public static void Test() { count = ; Task[] tasks = ]; ; i < tasks.Leng ...

  5. volatile、synchronized、lock有什么区别,以及在哪些场景下使用哪种方式?

    [转]JVM锁机制volatile/synchronized/lock 1.volatile实现原理 (1)聊聊并发(一)——深入分析Volatile的实现原理 --硬件级别锁实现,Lock前缀指令会 ...

  6. Java并发编程知识点总结Volatile、Synchronized、Lock实现原理

    Volatile关键字及其实现原理 在多线程并发编程中,Volatile可以理解为轻量级的Synchronized,用volatile关键字声明的变量,叫做共享变量,其保证了变量的“可见性”以及“有序 ...

  7. 线程、volatile与synchronized、Lock

    目录 线程 1.概念: 2.线程生命周期: 3.线程调度 4.线程实现 4.1.实现方式 4.2.之间的区别: 5.线程安全 5.1.volatile与synchronized 5.1.synchro ...

  8. C# 基础回顾: volatile 关键字

    有些人可能从来没看到过这个关键字,这也难怪,因为这个关键字并不常用.那这个关键字到底有什么用呢? 我在网上搜索这个关键字的时候,发现很多朋友都有一个错误的认识 ------ 认为这个关键字可以防止并发 ...

  9. C# Interlocked 笔记

    无锁代码下,在读写字段时使用内存屏障往往是不够的.在 64 位字段上进行加.减操作需要使用Interlocked工具类这样更加重型的方式.Interlocked也提供了Exchange和Compare ...

随机推荐

  1. EXCEL插件

    http://www.cnblogs.com/brooks-dotnet/category/233027.html http://www.360doc.com/content/15/0713/00/1 ...

  2. shell跑一个PHP脚本的简单命令

    最近在做一个刷数据库的小功能,需要批量添加到不同的表中,写好PHP文件之后,登录到某一个服务器上面 上传文件的命令:rz 会出现一个弹框可以选择要上传的文件 执行文件并报错误的命令:/usr/loca ...

  3. Asp.net主题(theme)和皮肤(skin)的使用

    asp.net 的服务器端控件提供了多种样式的设计,如果对每个控件都单独设置,是比较繁琐的事情,所以微软也提供了针对这些服务器端控件的样式管理,其实也可以通过 css来控制部分服务器端控件的样式,比如 ...

  4. DNS服务器的原理

    当用户去访问一个网站(百度)的时候,首先去请求dns服务器,根据对应的域名返回所在ip,然后再使用ip去访问自己所在服务器空间.简单的说,DNS服务器就像114客服,dns服务器是树状结构的,dns服 ...

  5. Express在windows IIS上部署详解

    最近公司在用Express+angularjs+wcf开发系统,让我在windows上部署系统,遇到不少问题,不过最后还是解决了,在IIS上部署系统, 首先windows需安装以下软件: 1.node ...

  6. jQuery实现页面元素智能定位

    实现过程 Js侦听滚动事件,当页面滚动的距离(页面滚动的高度)超出了对象(要滚动的层)距离页面顶部的高度,即要滚动的层到达了浏览器窗口上边缘时,立即将对象定位属性position值改成fixed(固定 ...

  7. linux常用命令(查看某些软件是否已安装)

    查看imap是否已安装 rpm -qa | grep imap 以下为未安装的情形: 检查是否已安装sendmail: rpm -qa | grep sendmail 以下为已安装的返回:

  8. 关于javascript输出中文乱码的问题

    今天找到一个引导效果.原来是用英文进行引导.但是我改了里面的英文为汉字就出现乱码的情况.英文提示是在js页面里面完成的.所以最后的解决办法 就是把js文件用记事本打开,然后把文件另存为utf-8的格式 ...

  9. C#遍历所有的Textbox控件并赋值为String.Empty

    foreach (Control control in this.Controls) { if (control.GetType().Name.Equals("TextBox")) ...

  10. ecshop--加载初始化文件

    define('IN_ECS', true);require(dirname(__FILE__) . '/../../includes/init.php'); 在开头要加入这两句文件才可以访问数据库以 ...