Java并发编程之CAS第三篇-CAS的缺点

通过前两篇的文章介绍,我们知道了CAS是什么以及查看源码了解CAS原理。那么在多线程并发环境中,的缺点是什么呢?这篇文章我们就来讨论讨论

本篇是《凯哥(凯哥Java:kagejava)并发编程学习》系列之《CAS系列》教程的第三篇:CAS的缺点有哪些?怎么解决。

CAS的缺点

一:do while循环时间长的话开销大

从源码中(见上图),我们可以知道do while中的while返回true会一直循环下去(具体分析步骤见上一篇:《Java并发编程之CAS二源码追根溯源》。凯哥(凯哥Java:kaigejava)就不在这里赘述了)。如果并发量很多的话,比如:有十万个线程来并发处理,这这种业务下,很多线程都会修改共享变量,要保证原子性的话,循环会很长时间,假设每个线程为了保证原子性,循环耗时0.001s的话,那么十万个线程都这么循环下来,对CPU的消耗还是比较大的。

二:只能保证一个共享变量的原子性

从源码中,我们知道 Object var1其实就是对象自己。拿上一篇文章举的例子来说,其实就是atomicInteger自己,也就是共享变量。CAS的do while只能一个this一个this的比较。从这里就可以看出,CAS只能保证一个共享变量的原子性。但是如果用同步锁的话,锁是可以锁对象也可以锁代码块。锁操作的可以不是一个共享变量。

三:会出现新的问题:ABA问题

何为ABA问题呢?

先来看看现实生活的例子:

学校举行运动会,标准操场一圈400米,现在正在进行1200米比赛。1200=400*3.需要跑上三圈。小明和小红比赛,在刚开始的时候,大家都看到小明,小红都在起点,但是小红速度比小明快2/3。这个时候,小明爸爸拿着相机拍摄,在起点时候,拍摄小明,3分钟过后,我们再来看起点,是小红。7分钟之后,在看起点是小明。难道小红就跑了一圈吗?这当然不对。小红比小明快,当我们第二次看到小明的时候,小红其实三圈已经跑完了。最终出现的情况就是:小明(小红)小红小明(小红)小明。最终获胜的当然是小红

这个例子或者不是很恰当。但是凯哥是想通过这个例子告诉大家,当线程如果出现这种情况的话,会影响到数据结果的。

如下图:

说明:

A线程执行一次耗时:1分钟

B线程执行一次耗时“29.5s

B线程在A线程执行一次的时间内操作主内存的数据变化为:202020192020

当B线程执行2次操作之后,1分钟到了。A线程拿着自己工作区copay的副本值i=2020和主内存的值i=2020。正好相等,这个时候会,主内存的共享变量相对于A线程来说,是没有变化的。但是实际上是有变化的(B线程确实操作过的。如上面举例的,小红已经跑完三圈了。可是小明才跑第二圈呢),如果这个时候在操作,有可能导致数据出问题(赛跑最终结果是小红赢了,而不是小明赢了)。

所谓的ABA就是:在某个监控点的时候数据是A,当过了时间N之后,在监控的时候还是A。但是在时间N的这段时间内,监控点的数据有可能不是A了,变成过B。这样就更容易理解了吧。

ABA问题演示代码:

代码说明:

初始的时候,给了变量值为2020.也就是V=2020.如上图1

在经过线程A一顿猛如虎的操作之后,搞出来2020,2021,2020.ABA的效果处理。如上图2.

Sleep了1秒是为了让线程A完成ABA操作的。

然后,线程2在拿着自己副本的变量值A=2020,和主内存V进行比较。发现一致,就更新了2019.

运行结果如下图:

从运行结果来看,线程2也更新成功了。但是,这样是不对的。因为我们已经知道线程A对共享变量操作过了。那么针对CAS的这些缺点,应该怎么解决呢?欢迎继续学习下一篇。凯哥将介绍三个怎么解决。以及会讲解原子引用、时间戳原子引用两个问题。

CAS缺点解决办法

一:循环长,开销大解决方案

解决思路:ConcurrentHashMap(后面凯哥也会详细介绍的)类似的方法。当多个线程竞争时,将粒度变小,将一个变量拆分为多个变量,达到多个线程访问多个资源的效果,最后再调用sum把它合起来。

二:一个共享变量的解决方案

因为CAS只能一个共享变量一个共享变量的处理。如果想要处理类是代码块或者对象的。可以使用同步锁或者是多个变量放到一个对象里面。然后在CAS。因为在JUC包下,有支持对象的原子类,如:AtomicReference(原子引用类)。

原子引用

在Java中变量的类型分为八大基本类型或八大基本类型的对象类型或者是自定义的对象类型。在并发中,atomicInteger就是基本类型就是int/Integer的原子类。那么自定义的对象怎么实现原子性呢?这就要用到原子引用对象- AtomicReference。

原子引用demo:

我们来模拟凯哥心中女神变化过程(注:女神同时只能存在一个,不能存在多个,要保持单一,原子的)。

在X年之前是刘亦菲,X+N年后是林依晨,现在是佟丽娅了。我们知道,这三个女神都是对象。都有年龄、用户名,是个对象。

创建user对象

她们三个在凯哥心中活动如下:

那么请问在21和23行输入的结果是什么?

编辑

我们发现在23行依然输出的是林依晨。而不是佟丽娅。为什么呢?分析思路见:《Java并发编程之CAS一理解》篇文章的三:cas代码演示部分。

我们修改之后再来看:

运行结果:

发现心中女神已经更新为佟丽娅了

三:ABA问题解决

ABA问题产生的根本原因是因为:只是线程自己工作空间的变量预期值(副本)和主内存中的值进行了比较。当值相等的时候,就默认没有被其他线程更新过。那么怎么解决这个问题呢?

是不是可以添加一个东西,用来辅助呢?添加一个标记,或者一个版本号,根据版本号+数值来进行判断呢?当然可以了,JDK中也是这么实现的。JDK使用的是时间戳(stamp),而不是我们说的版本号(version)。我们来看看时间戳原子引用(AtomicStampedReference<V>).

我们来看看这个类。

时间戳原子引用demo

先看构造器:

参数说明:

initialRef:初始值

initialStamp:初始值的时间戳

再来看看CompareAndSet方法:

参数说明:

expectedReference:预期值

newReference:更新值

expectedStamp:预期时间戳值

newStamp:更改后时间戳值

我们发现这个AtomicStampedReference类和AtomicReference的方法中的区别就是时间戳原子引用类中的方法都添加了预期的时间戳值和修改后的时间戳的值这两个参数。

我们来看看,使用带有时间戳的原子引用类解决ABA问题的代码:

1:声明共享变量

static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(127,1);

(需要说明,如果用数值做demo的话,主要int的取值范围。如果大于127,就会始终返回false。因为 Integer(128) == Integer(128)返回的是false)

线程一先修改执行一个ABA的过程:

编辑

执行完成之后,当前的主内存中版本号应该是3了。

我们在用线程2来执行compareAndSet:

此时,在线程2中的版本号:tamp应该是1,但是主内存中的版本号已经是3了。所以执行后返回false.执行不成功的。

我们来看看运行结果和我们预期结果:

运行结果,和我们预期结果是一致的。说明,添加这个时间戳(版本号)可以解决ABA问题

想要学习Java开发的同学,可以参考成都Java培训班提供的学习大纲;

Java并发编程之CAS第三篇-CAS的缺点相关推荐

  1. Java 并发编程之美:并发编程高级篇之一

    借用 Java 并发编程实践中的话:编写正确的程序并不容易,而编写正常的并发程序就更难了.相比于顺序执行的情况,多线程的线程安全问题是微妙而且出乎意料的,因为在没有进行适当同步的情况下多线程中各个操作 ...

  2. Java 并发编程之美:并发编程高级篇之一-chat

    借用 Java 并发编程实践中的话:编写正确的程序并不容易,而编写正常的并发程序就更难了.相比于顺序执行的情况,多线程的线程安全问题是微妙而且出乎意料的,因为在没有进行适当同步的情况下多线程中各个操作 ...

  3. zbb20180929 thread java并发编程之Condition

    java并发编程之Condition 引言 在java中,对于任意一个java对象,它都拥有一组定义在java.lang.Object上监视器方法,包括wait(),wait(long timeout ...

  4. Java并发编程之CyclicBarrier详解

    简介 栅栏类似于闭锁,它能阻塞一组线程直到某个事件的发生.栅栏与闭锁的关键区别在于,所有的线程必须同时到达栅栏位置,才能继续执行.闭锁用于等待事件,而栅栏用于等待其他线程. CyclicBarrier ...

  5. java并发编程之4——Java锁分解锁分段技术

    转载自 java并发编程之4--Java锁分解锁分段技术 并发编程的所有问题,最后都转换成了,"有状态bean"的状态的同步与互斥修改问题.而最后提出的解决"有状态bea ...

  6. Java并发编程之CAS和AQS

    什么是CAS CAS(compare and swap),字面意思比较并交换,是解决多线程并行情况下使用锁造成性能损耗的一种机制. public final boolean compareAndSet ...

  7. Java并发编程之synchronized关键字解析

    前言 公司加班太狠了,都没啥时间充电,这周终于结束了.这次整理了Java并发编程里面的synchronized关键字,又称为隐式锁,与JUC包中的Lock显示锁相对应:这个关键字从Java诞生开始就有 ...

  8. Java并发编程之AQS详解

    一.概述 谈到并发,不得不谈ReentrantLock:而谈到ReentrantLock,不得不谈AbstractQueuedSynchronizer(AQS)! 类如其名,抽象的队列式的同步器,AQ ...

  9. java并发编程之AbstractQueuedSynchronizer

    引言 AbstractQueuedSynchronizer,队列同步器,简称AQS,它是java并发用来构建锁或者其他同步组件的基础框架. 一般使用AQS的主要方式是继承,子类通过实现它提供的抽象方法 ...

最新文章

  1. OpenCV3.3中支持向量机(Support Vector Machines, SVM)实现简介及使用
  2. linux shell nr,awk中NR和FNR的区别小结和实例演示
  3. django ajax 简书,Django_ajax
  4. 2.1.5编码与调制(1)
  5. php 数组排序 按值,php – 按值排序数组
  6. $.getJSON()不执行回调函数
  7. 在Swift中向数组添加元素
  8. 【SAM】loj#6401. 字符串
  9. sql 日期和当前日期时间差_SQL基础进阶16日期处理
  10. 《WEB全栈工程师的自我修养》--索引笔记
  11. AFNetworking请求服务器错误
  12. 【通信协议】I2S/IIS总线介绍
  13. 复旦大学计算机学院邱锡鹏,复旦大学邱锡鹏教授为我院师生做学术报告
  14. ubuntu安装wechat
  15. 普中科技51单片机-单核-A2开发板实现简易计算器功能(矩阵键盘操作+数码管显示)
  16. 研究生跟了一个很棒的导师是种怎样的体验?
  17. 使用Origin画出复杂网络博弈中合作率时间演化图(学术论文)
  18. 基于spring+springmvc+mybatis的个人简易记账系统(包含数据库)
  19. 【R实验.7】回归分析
  20. [生存志] 第5节 第一篇 以史为鉴 罗振宇的思维逻辑

热门文章

  1. 哈佛计算机博士中国人有多少,《创4》选手学历曝光,哈佛、早稻田、清华都来了!却没有一个中国人...
  2. 院校-德国:亚琛工业大学(RWTH)
  3. 请设计一个算法,给一个字符串进行二进制编码,使得编码后字符串的长度最短。(哈夫曼树)...
  4. P2557 芝麻开门
  5. 国内科技公司不爱收购爱挖人?探索中外“贫富差距”背后的原因
  6. 填写设置小程序信息-微信小程序开发-视频教程3
  7. html实心三角符号,css实现实心三角形
  8. eliminate什么意思_eliminate是什么意思_ eliminate的翻译_音标_读音_用法_例句_爱词霸在线词典...
  9. 面向对象:余生很短,希望早点遇见你;余生很长,希望与你看尽人生百态
  10. 测试MBP的手写功能