CAS算法概述
CAS是英文单词CompareAndSwap的缩写,中文意思是:比较并替换。CAS需要有3个操作数:内存地址V,旧的预期值A,即将要更新的目标值B。

CAS指令执行时,当且仅当内存地址V的值与预期值A相等时,将内存地址V的值修改为B,否则就什么都不做。整个比较并替换的操作是一个原子操作。

注:t1,t2线程是同时更新同一变量56的值

因为t1和t2线程都同时去访问同一变量56,所以他们会把住内存的值完全拷贝一份到自己的工作内存空间,所以t1和t2线程值都为56

假设t1和t2在线程竞争中线程t1能去更新变量值改为57,而其他线程都失败。(失败的线程并不会被挂起,而是被告知这次竞争中失败,并可以再次发起尝试)。T1线程去更新变量值改为57,然后写到内存中。此时对于t2来说,内存值改为57,与预期值56不一致,就操作失败了(想改的值不再是原来的值)。

CAS算法的开销主要来自Cache Miss,一旦导致CAS一直更新失败的话,它的性能是有可能坏于加锁的方式的。

ABA问题概述

上面我我们说了CAS算法,CAS实现的过程是先取出内存中某时刻的数据,在下一时刻比较并替换,那么在这个时间差会导致数据的变化,此时就会导致出现“ABA”问题。关于“ABA”问题,我们假设如下事件序列:

线程 1 从内存位置V中取出A。
线程 2 从位置V中取出A。
线程 2 进行了一些操作,将B写入位置V。
线程 2 将A再次写入位置V。
线程 1 进行CAS操作,发现位置V中仍然是A,操作成功。
尽管线程 1 的CAS操作成功,但不代表这个过程没有问题——对于线程 1 ,线程 2 的修改已经丢失。

我们形象地画一个图来打个比方:

解决方法
我们在AtomicReference的使用中就遇到了这样的ABA问题,name怎么解决的呢?我们使用AtomicStampedReference就能很好的解决这个问题了,首先,我们先看一下这一段代码(实现自动充值,当少于20元时,充值20元,再进行消费,每次消费10元):

import java.util.concurrent.atomic.AtomicStampedReference;
public class AtomicReferenceDemo {
static AtomicStampedReference<Integer> money = new AtomicStampedReference<Integer>(19, 1);
static Object obj = new Object(); public static void main(String[] args) {
Add add = new Add();
Thread thread1 = new Thread(add);
thread1.start();
Reduce reduce = new Reduce();
Thread thread2 = new Thread(reduce);
thread2.start();
} static class Add implements Runnable { @Override
public void run() {while (true) {
Integer m = money.getReference();
synchronized (obj) { // 1处
if (m < 20) {
if (money.compareAndSet(m, m + 20, money.getStamp(), money.getStamp() + 1)) { //2处
System.out.println("充值成功,余额:" + money.getReference() + "元");
break;
}
} else {
break;
}
}
}
}
} static class Reduce implements Runnable { @Override
public void run() {
while (true) {
while (true) {
Integer m = money.getReference();
synchronized (obj) { //3处 if (m > 10) {
if (money.compareAndSet(m, m - 10, money.getStamp(), money.getStamp() + 1)) { //4处
System.out.println("成功消费10元,余额:" + money.getReference() + "元");
break;
}
} else {
break;
}
}
}
try {
Thread.sleep(1500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}

我们在1处2处加上锁之后就OK了!这是结果:

这样问题就很好的解决了,不过特别注意的是在1处和3处需要加锁,因为2处和4处的if条件是一个原子操作,大家都知道,java是抢占式的,线程可能在这个原子操作执行结束后被另一个线程所抢占,这样就是导致打印的时候的值不准确。

最新文章

  1. Android Studio导入第三方类库的方法(转)
  2. HDU 4738 Caocao&#39;s Bridges(Tarjan求桥+重边判断)
  3. android 入门 001 (界面布局)
  4. 一个好用的Log管理类
  5. 关于STM32的ST官方的库的一点看法
  6. Linux - 打印文件夹全部文件 代码(C)
  7. overflow: hidden用法,不仅仅是隐藏溢出
  8. React-Native OpenGL体验二
  9. 【NOIP2014提高组】联合权值
  10. DOM&amp;JavaScript示例&amp;练习
  11. 修改linux下yum镜像源为国内镜像
  12. Java JDK动态代理解析
  13. oracle 根据一个时间段获取这个时间段内所有月份、天数、日期
  14. PDF文件如何标注,怎么使用PDF标注工具
  15. net core2 采坑-- session 缓存
  16. 以太坊 链私有链环境搭建(windows)
  17. xpress for node 路由route几种实现方式
  18. 音视频处理之FFmpeg封装格式20180510
  19. 易普优APS与国外知名高级计划排程系统对比
  20. Python用于http/https接口自动化

热门文章

  1. 第四章 常用API(下)
  2. PHP password_hash() 函数
  3. loj #6247. 九个太阳 k次单位根 神仙构造 FFT求和原理
  4. Dockerfile你值得拥有
  5. 【HNOI2009】最小圈 题解(SPFA判负环+二分答案)
  6. adb如何连接Mac版腾讯手游助手
  7. python2.4项目:快递计价程序
  8. Python面向对象编程扑克牌发牌程序,另含大量Python代码!
  9. 2020-07-22:你觉得使用redis的主从复制的时候有什么点需要注意的吗?
  10. C#LeetCode刷题之#707-设计链表(Design Linked List)