Java并发:CAS与locking

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

据说CAS比locking方法好得多。 我想询问那些已经同时使用这两个概念的人,并且希望听到你更喜欢哪一个概念? 它真的太快了吗?

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

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

CAS通常比locking要快得多,但这取决于争用的程度。 因为如果读取和比较之间的值发生变化,CAS可能会强制重试,所以如果所讨论的variables被许多其他线程困难地攻击(或者如果计算新的值很昂贵从旧的价值(或两者))。

CAS的主要问题是正确编程比locking困难得多。 请注意,locking反过来比消息传递或STM更难正确使用,所以不要把它作为锁的使用。

这些操作的相对速度在很大程度上是不成问题的。 与基于锁的algorithm和非阻塞algorithm之间的可伸缩性有什么不同。 如果你正在运行一个或两个核心系统,就不要再想这样的事情了。

非阻塞algorithm一般比较好,因为它们比基于锁的algorithm具有更短的“关键部分”。

您可以查看ConcurrentLinkedQueue和BlockingQueue之间的数字。 你将会看到,CAS在中等程度(在现实世界的应用程序中更真实)的线程能力明显更快。

非阻塞algorithm最有吸引力的特性是,如果一个线程发生故障(高速caching未命中,或者更糟,seg故障),那么其他线程就不会注意到这个故障,并且可以继续运行。 但是,在获取锁的时候,如果锁保持线程有某种操作系统故障,那么等待锁释放的其他线程也会被中断。

要回答你的问题,是非阻塞的线程安全algorithm或集合(ConcurrentLinkedQueue,ConcurrentSkipListMap / Set)可以显着快于它们的阻塞对应。 正如Marcelo指出的那样,获得非阻塞algorithm是非常困难的,需要很多的考虑。

您应该阅读Michael和Scott Queue ,这是ConcurrentLinkedQueue的队列实现,并解释了如何使用单个CAS处理双向,线程安全的primefaces函数。

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

如果你正在寻找一个真实世界的比较,这里是一个。 我们的应用程序有两(2)个线程1)用于networking数据包捕获的读取器线程和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 

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