Java 并发: CAS 与锁定

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

据说 CAS 的性能比锁定方法好得多

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

我知道也许没有一个严格的规则什么时候使用什么

29900 次浏览

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

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

你可以看看 ConcurrentLinkedQueueBlockingQueue之间的数字。您将看到的是,CAS在适度(在实际应用程序中更现实)的线程争用下明显更快。

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

为了回答您的问题,是的,非阻塞线程安全算法或集合(ConcurrentLinkedQueueConcurrentSkipListMap/Set)比阻塞算法或集合快得多。正如 Marcelo 指出的那样,获得非阻塞算法的正确性是非常困难的,需要进行大量的考虑。

You should read about the Michael 和 Scott Queue, this is the queue implementation for ConcurrentLinkedQueue and explains how to handle a two-way, thread-safe, atomic function with a single 民安队.

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

操作的相对速度在很大程度上不是问题。相关的是基于锁和非阻塞算法之间在可伸缩性方面的差异。如果你在一个或者两个核心系统上运行,停止思考这些事情。

Nonblocking algorithms generally scale better because they have shorter "critical sections" than lock-based algorithms.

如果你正在寻找一个真实世界的比较,这里有一个。我们的应用程序有两个(2)线程1)一个用于网络数据包捕获的读线程和2)一个用户线程接收数据包,计数并报告统计数据。

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

结果 # 1 -使用一个定制的基于 CAS 的交换器,使用与 SynchronousQueue相同的原则,其中我们的类称为 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

我认为性能上的差异再清楚不过了。