CAS

CAS比通常的synchronized和lock速度更快么?

Usual locking with monitor-objects involves CAS as well as the locking kernel-calls. The cass alleviates the locking so that there will be no kernel call without contention. And when there is contention the CAS might spin for a time and then the kernel call would take place or the kernel call would follow immediately.

像一般锁的实现如通过monitor、或者一般的aqs实现,都会用到cas,所以,cas相当于更底层的机制,所以一般来说是要比一般锁的方式快的。通过自旋的方式,减少线程的切换,减少锁的竞争(写入同步队列或等待队列、然后在从队列中出来、修改state),从而提供性能的方式。

cas这种无锁算法,实现是比较复杂的,看看ConcurrentHashMap的无锁实现就知道了,好像有七千行。

The relative speed of the operations is largely a non-issue. What is relevant is the difference in scalability between lock-based and nonblocking algorithms. And if you’re running on a 1 or 2 core system, stop thinking about such things.

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

以上,另外一个观点,不通过锁的视角看,通过“critical sections”,临界区的概念上,非阻塞算法一般有更短的临界区。但是这个临界区其实比较难定义,并不是在cas的那小部分是临界区,cas也是有可见性的语义的。

在竞争非常激烈的场景下,cas也是有问题的,会有很多的自旋的浪费,但是锁竞争更是会性能下降。所以,一般来说cas性能上是由于锁的。

对于竞争非常激烈的情景,jdk8中有个LongAdder来解决部分问题,虽然只是利用空间换时间。

One or more variables that together maintain an initially zero long sum. When updates (method add(long)) are contended across threads, the set of variables may grow dynamically to reduce contention. Method sum() (or, equivalently, longValue()) returns the current total combined across the variables maintaining the sum.

能够动态的调整变量的个数,从而适应更大的竞争。

This class is usually preferable to AtomicLong when multiple threads update a common sum that is used for purposes such as collecting statistics, not for fine-grained synchronization control. Under low update contention, the two classes have similar characteristics. But under high contention, expected throughput of this class is significantly higher, at the expense of higher space consumption.

在low update contention的场景下,与AtomicLong特性差不多,在高竞争下,LongAdder的吞吐量更高。

LongAdders can be used with a ConcurrentHashMap to maintain a scalable frequency map (a form of histogram or multiset). For example, to add a count to a ConcurrentHashMap<String,LongAdderfreqs, initializing if not already present, you can use freqs.computeIfAbsent(k -new LongAdder()).increment();

参考

java-concurrency-cas-vs-locking

Table of Contents