易失性与联锁对抗锁定

喜你入骨 提交于 2019-12-29 19:40:19

【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>>

假设一个类有一个由多个线程访问的public int counter字段。 此int仅递增或递减。

要增加此字段,应使用哪种方法,为什么?

  • lock(this.locker) this.counter++;
  • Interlocked.Increment(ref this.counter);
  • counter的访问修饰符更改为public volatile

既然我发现了volatile ,我一直在删除许多lock语句和Interlocked的使用。 但是有理由不这样做吗?


#1楼

我是第二个Jon Skeet的回答,并希望为想要了解更多关于“volatile”和Interlocked的人们添加以下链接:

原子性,波动性和不变性是不同的,第一部分 - (Eric Lippert的编码中的神话般的冒险)

原子性,波动性和不变性是不同的,第二部分

原子性,波动性和不变性是不同的,第三部分

Sayonara Volatile - (2012年出现的Joe Duffy博客的Wayback Machine快照)


#2楼

互锁功能不会锁定。 它们是原子的,这意味着它们可以完成而不会在增量期间进行上下文切换。 所以没有死锁或等待的可能性。

我会说你应该总是喜欢锁定和增量。

如果您需要在一个线程中写入以在另一个线程中读取,并且您希望优化器不对变量重新排序操作(因为事情发生在优化器不知道的另一个线程中),则Volatile非常有用。 这是你如何增量的正交选择。

如果您想要阅读更多关于无锁代码的信息,以及正确的编写方法,这是一篇非常好的文章

http://www.ddj.com/hpc-high-performance-computing/210604448


#3楼

编辑:正如评论中所指出的,这些天我很乐意使用Interlocked来处理单个变量的情况 ,这显然是可以的。 当它变得更复杂时,我仍然会恢复锁定......

当您需要递增时,使用volatile将无济于事 - 因为读取和写入是单独的指令。 另一个线程可能会在您阅读之后但在您回写之前更改该值。

就我个人而言,我几乎总是只是锁定 - 以一种明显正确的方式,比波动性或Interlocked.Increment更容易正确。 就我而言,无锁多线程是真正的线程专家,其中我不是一个。 如果Joe Duffy和他的团队构建了一个很好的库,这些库可以在没有像我构建的东西那么多锁定的情况下进行并行化,这很棒,而且我会在心跳中使用它 - 但是当我自己进行线程处理时,我会尝试把事情简单化。


#4楼

lock(...)有效,但可能阻塞一个线程,如果其他代码以不兼容的方式使用相同的锁,则可能导致死锁。

Interlocked。*是正确的方法...因为现代CPU支持它作为原语,所以开销要少得多。

挥发性本身是不正确的。 尝试检索然后写回修改值的线程仍然可能与执行相同操作的另一个线程冲突。


#5楼

volatile ”不会取代Interlocked.Increment ! 它只是确保变量不缓存,而是直接使用。

增加变量实际上需要三个操作:

  1. 增量

Interlocked.Increment所有三个部分作为单个原子操作执行。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!