Java --- JUC之CAS
目录
一、CAS
二、CAS原理
三、CAS简单使用
四、CAS的实现
4.1、硬件级别保证
4.2、源码分析
五、Unsafe类
六、CAS的AtomicReference的使用
七、使用CAS实现自旋锁
八、CAS缺点
8.1、循环时间长开销很大
8.2、CAS会导致”ABA问题“
九、CAS的AtomicStampedReference解决ABA问题
一、CAS
compare and swap的缩写,表示:比较并交换,实现并发算法时常用的一种技术。
它包含三个操作数----内存位置、预期原值及更新值。
执行CAS操作的时候,将内存位置的值与预期原值比较;
如果相匹配,那么处理器会自动将该位置值更新为新值。
如果不匹配,处理器不做任何操作,多个线程同时执行CAS操作只会有一个会成功。
二、CAS原理
CAS的3个操作数,位置内存值V,旧的预期值A,要修改的更新值B。
当且仅当旧的预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做或重来当它重来重试的这种行为------自旋。
三、CAS简单使用
public class CASTest {public static void main(String[] args) {AtomicInteger atomicInteger = new AtomicInteger(2);System.out.println(atomicInteger.compareAndSet(2, 255) + "\t" + atomicInteger.get());System.out.println(atomicInteger.compareAndSet(2, 255) + "\t" + atomicInteger.get());}
}
四、CAS的实现
4.1、硬件级别保证
CAS是JDK提供的非阻塞原子性操作,它通过硬件保证了比较-更新的原子性。
它是非阻塞的且自身具有原子性,也就是说它效率更高且通过硬件保证,说明更可靠。
CAS是一条CPU的原子指令(cmpxchg指令),不会造成所谓的数据不一致性问题,Unsafe提供的CAS方法底层实现即为CPU指令cmpxchg。
执行cmpxchg指令的时候,会判断当前系统是否为多核系统,如果是就给总线加锁,只有一个线程会对总线加锁成功,加锁成功之后会执行CAS操作,也就是说CAS的原子性实际上是CPU实现独占的,比用synchronized重量级锁,这里的排他时间要短很多,所以在多线程情况下性能会比较好
4.2、源码分析
compareAndSet()方法的源代码
参数说明:
var1:要操作的对象
var2:要操作对象中属性地址的偏移量
var4:需要修改数据的期望的值
var5/var6:表示需要修改为的新值
五、Unsafe类
1、是CAS的核心类,由于Java方法无法直接访问底层操作系统,需要通过本地(native)方法来访问,Unsafe相当于一个后门,基于该类可以直接操作特定内存的数据。Unsafe类存在于sun.misc包中,其内部方法操作可以像C的指针一样直接操作内存,因为Java中CAS操作的执行依赖于Unsafe类的方法。
2、变量valueOffset:该变量值在内存中的偏移地址,因为Unsafe就是根据内存偏移地址获取数据的。
3、变量value使用volatile修饰,保证了多线程之间的内存可见性。
六、CAS的AtomicReference的使用
public class CASTest1 {public static void main(String[] args) {AtomicReference<People> atomicReference = new AtomicReference<>();People people1 = new People("小明", 22);People people2 = new People("小红", 21);atomicReference.set(people1);System.out.println(atomicReference.compareAndSet(people1, people2) + "\t" + atomicReference.get().toString());System.out.println(atomicReference.compareAndSet(people1, people2) + "\t" + atomicReference.get().toString());}
}class People {private String name;private int age;public People(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}@Overridepublic String toString() {return "People{" +"name='" + name + '\'' +", age=" + age +'}';}
}
七、使用CAS实现自旋锁
CAS是实现自旋锁的基础,CAS利用CPU指令保证了操作的原子性,以达到锁的效果,至于自旋:就是自己旋转,指尝试获取锁的线程不会立即阻塞,而是采用循环的方式去尝试获取锁,当线程发现锁被占用时,会不断循环判断锁的状态,直接获取。这样的好处就是减少线程上下文切换的消耗,缺点就是循环消耗CPU
public class CASTest2 {AtomicReference<Thread> atomicReference = new AtomicReference<>();public void lock(){Thread thread = Thread.currentThread();while (!atomicReference.compareAndSet(null,thread)){}}public void unLock(){Thread thread = Thread.currentThread();atomicReference.compareAndSet(thread,null);}public static void main(String[] args) {CASTest2 casTest2 = new CASTest2();new Thread(()->{System.out.println(Thread.currentThread().getName() + "\t 开始");casTest2.lock();try {TimeUnit.SECONDS.sleep(5);} catch (InterruptedException e) {e.printStackTrace();}casTest2.unLock();System.out.println(Thread.currentThread().getName() + "\t 结束");},"A").start();new Thread(()->{System.out.println(Thread.currentThread().getName() + "\t 开始");casTest2.lock();casTest2.unLock();System.out.println(Thread.currentThread().getName() + "\t 结束");},"B").start();}
}
八、CAS缺点
8.1、循环时间长开销很大
上述方法使用的do while,如果CAS失败,会一直进行尝试。如果CAS长时间一直不成功,可能会给CPU带来很大的开销。
8.2、CAS会导致”ABA问题“
CAS算法实现一个重要前提需要取出内存中某时刻的数据并在当下时刻比较并替换,那么在这个时间差类会导致数据的变化。
如一个线程1从内存位置V中取出A,这时候另一个线程2也从内存中取出A,并且线程2进行了一些操作将值变为了B,然后线程2又将V位置的数据变为A,这时候线程1进行CAS操作发现内存中任然是A,预期OK,然后线程1操作成功。结果虽然正确,但过程不一定没有问题
九、CAS的AtomicStampedReference解决ABA问题
单线程解决:
public class CASTest3 {public static void main(String[] args) {Book java = new Book(1, "java");Book go = new Book(2, "go");boolean b;AtomicStampedReference<Book> atomicStampedReference = new AtomicStampedReference<>(java, 1);System.out.println(atomicStampedReference.getReference() + "\t" + atomicStampedReference.getStamp());b = atomicStampedReference.compareAndSet(java, go, atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1);System.out.println(b+"\t"+atomicStampedReference.getReference() + "\t" + atomicStampedReference.getStamp());b = atomicStampedReference.compareAndSet(go, java, atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1);System.out.println(b+"\t"+atomicStampedReference.getReference() + "\t" + atomicStampedReference.getStamp());}
}
class Book{private Integer id;private String name;public Book(Integer id, String name) {this.id = id;this.name = name;}public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}@Overridepublic String toString() {return "Book{" +"id=" + id +", name='" + name + '\'' +'}';}
}
多线程:
public class CASTest4 {static AtomicStampedReference stampedReference = new AtomicStampedReference(100,1);public static void main(String[] args) {new Thread(()->{int stamp = stampedReference.getStamp();System.out.println(Thread.currentThread().getName() + "\t第一次版本号" + stamp);try {TimeUnit.MILLISECONDS.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}boolean b;b = stampedReference.compareAndSet(100, 101, stampedReference.getStamp(), stampedReference.getStamp() + 1);System.out.println(b+"\t"+stampedReference.getReference()+"\t"+stampedReference.getStamp());b = stampedReference.compareAndSet(101, 100, stampedReference.getStamp(), stampedReference.getStamp() + 1);System.out.println(b+"\t"+stampedReference.getReference()+"\t"+stampedReference.getStamp());},"A").start();new Thread(()->{int stamp = stampedReference.getStamp();System.out.println(Thread.currentThread().getName() + "\t第一次版本号" + stamp);try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}boolean b = stampedReference.compareAndSet(100, 101, stamp, stamp + 1);System.out.println(b+"\t"+stampedReference.getReference()+"\t"+stamp);},"B").start();}
}
Java --- JUC之CAS相关推荐
- JAVA并发编程: CAS和AQS
说起JAVA并发编程,就不得不聊聊CAS(Compare And Swap)和AQS了(AbstractQueuedSynchronizer). CAS(Compare And Swap) 什么是CA ...
- Java JUC学习 - ConcurrentLinkedDeque 详解
Java JUC学习 - ConcurrentLinkedDeque 详解 0x00 前言 如何实现并发程序,对于Java以及其他高级语言来说都是一件并不容易的事情.在大一上学期的时候,我们学习了链表 ...
- 【高并发】java中的CAS,你需要知道的东西
1.概述 转载:添加链接描述 从网站计数器实现中一步步引出CAS操作 介绍java中的CAS及CAS可能存在的问题 悲观锁和乐观锁的一些介绍及数据库乐观锁的一个常见示例 使用java中的原子操作实现网 ...
- Java中的CAS以及AQS实现原理
Java中的CAS实现原理 什么是CAS? 在计算机科学中,比较和交换(Conmpare And Swap)是用于实现多线程同步的原子指令. 它将内存位置的内容与给定值进行比较,只有在相同的情况下,将 ...
- Java juc系列6 —— 线程池
Java JUC系列目录链接 Java 线程池核心原理解析 Java线程池的基础用法 创建和使用 为什么需要线程池 线程的生命周期[^1] 新建 就绪 运行 休眠 终止 使用线程的代价 线程池帮我们做 ...
- Java JUC系列
1.Java JUC 简介 2.volatile 关键字-内存可见性 3.原子变量 CAS算法 4.ConcurrentHashMap 锁分段机制 5.CountDownLatch 闭锁 6.实现 C ...
- 【面试篇】Java多线程并发-Java中的CAS机制算法
Java中的CAS机制算法 a.CAS例子 再讲解CAS机制之前,先来看一道经典的并发执行1000次递增的问题: public class Test { public static int count ...
- JAVA 中的 CAS
原文地址:https://www.xilidou.com/2018/02/01/java-cas/ CAS 是现代操作系统,解决并发问题的一个重要手段,最近在看 eureka 的源码的时候.遇到了很多 ...
- java 获取 jndi_Websphere下获取jndi、java:comp/env/cas与jdbc/cas的区别
Websphere下获取jndi,java:comp/env/cas与jdbc/cas的区别: A.lookup("java:comp/env/cas")与lockup(" ...
最新文章
- 一b变频器说明书参数一览表_电工常见的23个变频器问题整理,附专业解答
- 自动加载 autoload
- wxWidgets:wxMiniFrame类用法
- 做了一个网页,顺便录制了一个视频,大家看看高端不
- 怎么使用socket在云服务上通信步骤(可支持TCP或UDP)
- 基于PHP MySQL在线考试系统,基于PHP和MySQL的考试系统的研究
- class12_pack_grid_place 放置位置
- 象过河软件试用版_比肩许银川蒋川王天一,象棋软件下出神一样的残局,看完叹为观止...
- paip.gui控件tabs控件加载内容的原理以及easyui最佳实现
- TiDB 部署及数据同步
- python 拆分字符串反斜杠_每日一课 | Python 拆分字符串入到字典里面
- 编码格式详解:多字节字符集和Unicode字符集
- 科研人员论文投稿邮箱选择的问题
- 梦幻西游进入游戏显示服务器程序停止工作,win7系统提示“梦幻西游已停止工作”的解决方法...
- 【硬见小百科】数字电子时钟电路图设计原理
- 【中医学】11 常见病证-2:心悸:眩晕:中风:血证:黄疸:痹证:消渴
- MongoTemplate地理位置查询(标准)
- python syntactic suger
- 无线路由器如何建立ftp服务器,利用无线路由器建立FTP服务器
- 国际期货和国内期货的区别带招商
热门文章
- 不同浏览器下word-wrap和word-break强制换行
- python爬取MOOC课程信息
- zzuli 1787: 生化危机 (bfs与dfs)
- linux系统 说法不正确的是,下面关于Linux操作系统的说法中,不正确的是( )。
- 硬件调试-手动吸锡枪和电动吸锡枪注意事项
- Android elevation使用
- kali局域网APR攻击三https降级为http+网站账号密码获取
- iOS音频播放 (三):AudioFileStream 转
- 输油管道或水井挖水渠中路径和最短问题
- 写博客的主要动力来源