Java并发:CAS与锁定[关闭]


76

我正在阅读《实践Java并发》一书。在第15章中,他们讨论了非阻塞算法和比较交换(CAS)方法。

据记载,CAS的性能比锁定方法好得多。我想问一下已经使用这两个概念的人,并想听听您更喜欢这些概念中的哪一个?真的那么快吗?

对我来说,锁的用法更加清晰,易于理解,甚至维护起来也更好(如果我错了,请更正我)。我们是否应该真正专注于创建与CAS相关的并发代码,而不是锁定,以获得更好的性能提升,或者可持续性更重要?

我知道何时使用什么可能没有严格的规定。但是我只是想听听一些有关CAS新概念的意见和经验。


通常使用监视对象进行锁定涉及CAS以及锁定内核调用。Cass减轻了锁定,因此没有争用就不会有内核调用。并且当存在争用时,CAS可能会旋转一段时间,然后发生内核调用或立即进行内核调用。
Bonita Montero,

Answers:


46

通常,CAS比锁定快得多,但是它确实取决于争用程度。因为如果数值在读取和比较之间变化,CAS可能会强制重试,因此,如果所讨论的变量被许多其他线程重击(或者计算新值的成本很高),则该线程理论上可能陷入忙碌等待从旧值(或两者兼有)。

CAS的主要问题是正确编程比锁定困难得多。请注意,与使用消息传递或STM相比,锁定反过来更难正确使用,因此请不要将其视为对使用锁的敲打赞同。


1
我同意,把重点放在计算新价值上。通常它是如此之大,使得非锁定策略失去了基于锁定的能力。
Alex Salauyou 2015年

28

操作的相对速度在很大程度上不是问题。相关的是基于锁的算法和非阻塞算法之间在可伸缩性方面的差异。而且,如果您在1或2核心系统上运行,请不要考虑这些事情。

非阻塞算法通常可以更好地扩展,因为与基于锁的算法相比,它们的“关键部分”更短。


23

您可以查看介于ConcurrentLinkedQueue和之间的数字BlockingQueue。您将看到,在中等(在实际应用中更现实)线程争用下,CAS明显更快。

非阻塞算法最吸引人的特性是,如果一个线程失败(高速缓存未命中,或更糟糕的是,段错误),那么其他线程将不会注意到该失败,并且可以继续下去。但是,在获取锁时,如果锁保持线程发生某种操作系统故障,则其他所有等待释放锁的线程也都将遭受失败。

要回答您的问题,是的,非阻塞线程安全算法或集合(ConcurrentLinkedQueueConcurrentSkipListMap/Set)可能比阻塞对象更快。正如Marcelo指出的那样,正确地实现非阻塞算法是非常困难的,需要很多考虑。

您应该阅读有关Michael和Scott队列的信息,这是该队列的实现,ConcurrentLinkedQueue并说明了如何使用单个CAS处理双向线程安全的原子函数。


1
关于“ Michael and Scott Queue”的文章很有趣。谢谢!
Prine


9

如果您正在寻找现实世界的比较,这里是一个。我们的应用程序有两个(2)线程:1)用于捕获网络数据包的读取器线程,以及2)接收数据包,对其进行计数并报告统计信息的使用者线程。

线程1与线程2一次交换一个数据包

结果#1-使用与SynchronousQueue相同的原理使用基于CAS的自定义交换,我们的类称为CASSynchronousQueue

30,766,538 packets in 59.999 seconds ::  500.763Kpps, 1.115Gbps 0 drops
libpcap statistics: recv=61,251,128, drop=0(0.0%), ifdrop=0

结果2-当我们用标准的Java SynchronousQueue替换CAS实现时:

8,782,647 packets in 59.999 seconds ::  142.950Kpps, 324.957Mbps 0 drops 
libpcap statistics: recv=69,955,666, drop=52,369,516(74.9%), ifdrop=0

我认为性能上的差异再也看不到了。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.