78

我正在阅读Java Concurrency in Practice一书。在第 15 章中,他们讨论了非阻塞算法和比较和交换(CAS) 方法。

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

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

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

4

5 回答 5

51

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

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

于 2010-04-18T22:22:21.410 回答
29

操作的相对速度在很大程度上不是问题。相关的是基于锁的算法和非阻塞算法在可扩展性方面的差异。如果您在 1 或 2 核系统上运行,请停止考虑此类事情。

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

于 2010-04-29T00:01:51.590 回答
24

您可以查看 aConcurrentLinkedQueue和 a之间的数字BlockingQueue。您将看到的是,在中等(在现实世界的应用程序中更现实)线程争用情况下,CAS明显更快。

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

要回答您的问题,是的,非阻塞线程安全算法或集合 ( ConcurrentLinkedQueue, ConcurrentSkipListMap/Set) 可能比它们的阻塞对应物快得多。正如 Marcelo 指出的那样,使非阻塞算法正确是非常困难的,需要大量考虑。

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

于 2010-04-18T23:00:49.620 回答
14

有一本与无锁并发主题密切相关的好书:Maurice Herlihy 的“多处理器编程艺术”

于 2010-04-22T10:15:38.140 回答
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

我认为性能上的差异再明显不过了。

于 2015-07-20T03:12:17.910 回答