博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
CAS
阅读量:3751 次
发布时间:2019-05-22

本文共 3981 字,大约阅读时间需要 13 分钟。

文章目录

本文介绍了CAS算法的应用场景及原理。

1、volatile不能保证原子性

假设有个场景,在单实例环境下,记录访问某接口的用户数。为此,设计了一个计数器,代码如下:

public class MyApp {
/* volatile 不能保证变量复合操作的原子性 */ private volatile int count = 0; public void updateVistors() {
count++; }}

虽然volatile修饰的共享变量count的内存可见性能被保证,但count++操作的原子性则不然,当多线程并发修改count值时,count是线程不安全的。

2、synchronized是大开销操作

解决这个问题,不少人的第一反应是将count++的操作加锁(synchronized),如下:

public class MyApp {
private int count = 0; /* 带锁的方法 synchronized */ public synchronized void updateVistors() {
count++; }}

当然,synchronized关键字修饰后,count++操作的原子性可见性都能被保证,但synchronized是相对重的锁。只是为了实现小小的自增需求,就引入该锁显得“大材小用”。即使在JDK1.6之后,synchronized已经因为引入了偏向锁、轻量级锁而被优化很多,但当多线程激烈竞争时,大量线程阻塞、唤醒,相对“无锁并发”实际是大开销的操作。

3、CAS(原子类)解决方案

假如使用线程安全的原子类,由于使用“无锁并发”技术(CAS),开销相对synchronized要小很多。

public class MyApp1 {
private final AtomicLong al = new AtomicLong(0); public void updateVisitors() {
al.incrementAndGet(); }}

相对于synchronized这样的pessimistic locking(悲观锁),CAS是一种更轻量的optimistic locking(乐观锁)技术。

CAS(Compare and Swap)技术的核心思想如下:

This algorithm compares the contents of a memory location to a given

value and, only if they are the same, modifies the contents of that
memory location to a given new value.

This is done as a single atomic operation. The atomicity

guarantees that the new value is calculated based on up-to-date
information; if the value had been updated by another thread in the
meantime, the write would fail.

The result of the operation must indicate whether it performed the

substitution; this can be done either with a simple Boolean response
(this variant is often called compare-and-set), or by returning
the value read from the memory location (not the value written to it).

  1. A memory location V where value has to be replaced
  2. Old value A which was read by thread last time
  3. New value B which should be written over V

CAS says “I think V should have the value A; if it does, put B there,

otherwise don’t change it but tell me I was wrong.” CAS is an
optimistic technique—it proceeds with the update in the hope of
success, and can detect failure if another thread has updated the
variable since it was last examined.

画成图:

在这里插入图片描述

4、原子类的核心源码分析

在JDK8之前,代码如是写道:

public final long incrementAndGet() {
for (;;) {
long old = get(); // the value read by thread last time long next = old + 1; // new value to be written over V if (compareAndSet(current, next)) return next; }}

JDK8之后,采用了更能说明其本质的写法:

public final long incrementAndGet() {
return unsafe.getAndAddLong(this, valueOffset, 1L) + 1L;}

这样写有什么好处呢?

unsafe.getAndAddLong()会被JIT优化。在x86架构下,这句代码只对应到一句CPU指令LOCK XADD,这个指令比普通的循环CAS的性能更好。

在多线程激烈竞争的场景下,synchronized可能比原子变量性能更好(多线程空自旋会影响性能?),不过多数情况下(in realistic contention levels )原子变量比synchronized性能更好。

Java8还引入了LongAdder类,其表述如下:

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.

So LongAdder is not always a replacement for AtomicLong. We need to

consider the following aspects:

1)When no contention is present AtomicLong performs better.

2)LongAdder will allocate Cells (a final class declared in
abstract class Striped64) to avoid contention which consumes memory.
So in case we have a tight memory budget we should prefer AtomicLong.

这段话抓住重点:

  • 1)激烈冲突下,LongAdder吞吐更高,但是消耗内存更大
  • 2)低冲突下,使用AtomicLong
  • 3)在类似数据统计,而不是精确同步的场景下,更推荐LongAdder

5、CAS的问题

CAS的问题有三:

  • 1)ABA问题
  • 2)多线程长期自旋忙等锁,造成CPU开销大
  • 3)CAS只能对单个变量实现原子操作

在专题文章 【】中已有说明

6、更深入:CAS的原理

CAS通过调用JNI的代码实现的。JNI:Java Native Interface为JAVA本地调用,允许java调用其他语言。

compareAndSwapInt就是借助C来调用CPU底层指令实现的。
(具体不再深入)

7、CAS在并发包的地位

CAS + AQS是并发包的基石!

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nQq4aot1-1618652367800)(http://note.youdao.com/yws/res/36415/WEBRESOURCE9804eb32fef3cca7be224d1305445907)]

8、CAS思想在业务代码中的应用

参考文献

  • https://dzone.com/articles/how-cas-compare-and-swap-java

转载地址:http://cknsn.baihongyu.com/

你可能感兴趣的文章
银行业务队列简单模拟(队列queue)
查看>>
MySql中的数据查询语言(DQL)三:连接查询
查看>>
MySql中的数据查询语言(DQL)五:union和limit
查看>>
数据操作语言(DML)一:插入数据insert、修改数据update、删除delete
查看>>
.properties 文件,.yml 文件 ,yaml文件语法学习
查看>>
jsp 的常用标签
查看>>
Listener 监听器
查看>>
SpringBoot自动配置原理
查看>>
IDEA连接mysql又报错设置时区!Server returns invalid timezone.
查看>>
员工管理系统二:首页和国际化实现
查看>>
员工管理系统四:员工列表实现
查看>>
员工管理系统五:增删改员工实现
查看>>
Redis的安装与卸载
查看>>
项目阶段五:验证码
查看>>
项目阶段五:购物车
查看>>
项目阶段六:订单模块的数据库准备与dao、service层
查看>>
项目阶段六:后台管理的订单模块
查看>>
练习——图书管理系统八(根据图书编号填充图书名称下拉控件和验证手机号)
查看>>
将windows下文件上传至服务器中
查看>>
正则表达式:贪婪模式与懒惰模式
查看>>