一、什么是CAS

  Compare and Swap, 翻译成比较并交换,是java.util.concurrent.atomic包下的类里面的CompareAndSet()方法;java.util.concurrent包中借助CAS实现了区别于synchronouse同步锁的一种乐观锁,使用这些类在多核CPU的机器上会有比较好的性能。

二、小例子

  把2020交换为2021,之后获取操作后的值。再重复一次查看效果

public class MyDemo {
// CAS compareAndSet : 比较并交换!
public static void main(String[] args) {
AtomicInteger atomicInteger = new AtomicInteger(2020);
// 期望、更新
// public final boolean compareAndSet(int expect, int update)
// 如果我期望的值达到了,那么就更新,否则,就不更新, CAS 是CPU的并发原语!
System.out.println(atomicInteger.compareAndSet(2020, 2021));
System.out.println(atomicInteger.get());
atomicInteger.getAndIncrement();
System.out.println(atomicInteger.compareAndSet(2020, 2021));
System.out.println(atomicInteger.get());
}
}

  在java.util.concurrent.atomic包下的任意类调用CompareAndSet()方法,都是调用Unsafe类里面的方法,这个类是底层的,Java调用C++操作。

  在调用getAndIncrement()方法后,方法里面调用unsafe.getAndAddInt(this, valueOffset, 1);到Unsafe类里面操作public final int getAndAddInt(Object var1, long var2, int var4)

方法,其中【this---var1,valueOffset---var2,1---var4】,在这个方法里面,通过var5 = this.getIntVolatile(var1, var2);获取内存地址中的值,然后再调用this.compareAndSwapInt(var1, var2, var5, var5 + var4),使其为真。

  缺点:

    1、循环会耗时

    2、一次性只能保证一个共享变量的原子性

    3、ABA问题----就是多线程下,例如有3个线程同时对同一个值(初始值为A)进行CAS操作,其中两个线程:线程1,期望值为A,欲更新的值为B;线程2,期望值为A,欲更新的值为B。当线程1抢先获得CPU时间片,而线程2因为其他原因阻塞了,线程1取值与期望的A值比较,发现相等然后将值更新为B,然后这个时候出现了线程3,期望值为B,欲更新的值为A,线程3取值与期望的值B比较,发现相等则将值更新为A,此时线程2从阻塞中恢复,并且获得了CPU时间片,这时候线程2取值与期望的值A比较,发现相等则将值更新为B,虽然线程2也完成了操作,但是线程2并不知道值已经经过了A->B->A的变化过程。

  如何解决ABA问题:在变量前面加上版本号,每次变量更新的时候变量的版本号都+1,即A->B->A就变成了1A->2B->3A。(这就是乐观锁的思想)
三、利用AtomicStampedReference<V> 类解决ABA
public class MyDemo {
static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(1, 1); // CAS compareAndSet : 比较并交换!
public static void main(String[] args) { new Thread(() -> {
int stamp = atomicStampedReference.getStamp(); // 获得版本号
System.out.println("a1=>" + stamp);
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
atomicStampedReference.compareAndSet(1, 2,
atomicStampedReference.getStamp(),
atomicStampedReference.getStamp() + 1);
System.out.println("a2=>" + atomicStampedReference.getStamp());
System.out.println(atomicStampedReference.compareAndSet(2, 1,
atomicStampedReference.getStamp(),
atomicStampedReference.getStamp() + 1));
System.out.println("a3=>" + atomicStampedReference.getStamp());
}, "a").start(); // 乐观锁的原理相同!
new Thread(() -> {
int stamp = atomicStampedReference.getStamp(); // 获得版本号
System.out.println("b1=>" + stamp);
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(atomicStampedReference.compareAndSet(1, 6,
stamp, stamp + 1));
System.out.println("b2=>" + atomicStampedReference.getStamp());
}, "b").start();
}
}

最新文章

  1. 【吉光片羽】之 Web API
  2. uva624 01背包要求输出路径
  3. (转)【Unity3d】Glow + Outline 轮廓描边
  4. JSON的三种解析方式
  5. 【转载】标准输入输出重定向(Visual C++)
  6. tar.xz如何解压:linux和windows下tar.xz解压命令介绍
  7. 函数lock_rec_get_n_bits
  8. Jave 鼠标点击画太极 PaintTaiji (整理)
  9. SomeThing of Memcache
  10. linux访问windows共享文件夹的两种方法
  11. 关于MAC的pkg和mpkg的分别
  12. 关于arguments.callee的用途
  13. c++ 实现将数字转换为中文数字输出
  14. 原生ajax详解
  15. Unity3D手机斗地主游戏开发实战(03)_地主牌显示和出牌逻辑(不定期更新中~~~)
  16. [leetcode]432. All O`one Data Structure全O(1)数据结构
  17. shell 日常技巧
  18. HRBUST - 2347 - 递归画图 - vj大一上寒假训练2.11
  19. vCenter 5.1 U1 Installation: Part 9 (vCenter SSO Configuration)
  20. Azure编程笔记(3):用Fiddler调试Azure的应用程序

热门文章

  1. Spring学习(五)bean装配详解之 【XML方式配置】
  2. 优酷kux转mp4
  3. Java源码赏析(五)再识 String 类
  4. gRPC-Protocol语法指南
  5. JSP2.2自定义标签、EL函数
  6. 类名@6d5037a9(地址)
  7. Linux安装软件方法总结
  8. 一种基于均值不等式的Listwise损失函数
  9. arduino 动态内存不足问题
  10. Jmeter之连接数据库