并发编程CAS操作
并发编程CAS操作
简介
CAS即compare and swap,中文就是比较并交换
CAS是Java并发包的基石
原理
其实CAS的原理相对来说比较简单。将要被改变的数据和期望的值作比较,当两个值相等时,再将数值替换成新值。
其实通俗的来讲就是"我认为原有的值是什么样子,如果一样则将原有的值更换成新值,否则不做修改,并返回原有的值"。这一系列操作是原子的。
CAS 指的是现代CPU广泛支持的一种对内存中的共享数据进行操作的一种特殊指令
CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则返回V。这是一种乐观锁的思路
使用CAS可以实现非阻塞无锁的方式实现原子操作
实例
在java并发包中有java.util.concurrent.atomic这样一个包,该包是对Java部分数据类型的原子封装,通过CAS提供了原子性的操作方法,保证了线程安全。
如下代码
public class AtomicInteger extends Number implements java.io.Serializable {
private static final long serialVersionUID = 6214790243416807050L;
// 使用Unsafe包来实现CAS操作
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long valueOffset;
static {
try {
valueOffset = unsafe.objectFieldOffset
(AtomicInteger.class.getDeclaredField("value"));
} catch (Exception ex) { throw new Error(ex); }
}
//使用volatile修饰符保证值value的线程可见性
private volatile int value;
/**
* Creates a new AtomicInteger with the given initial value.
*
* @param initialValue the initial value
*/
public AtomicInteger(int initialValue) {
value = initialValue;
}
/**
* Creates a new AtomicInteger with initial value {@code 0}.
*/
public AtomicInteger() {
}
/**
* 获得当前值
*
* @return the current value
*/
public final int get() {
return value;
}
/**
* 设置值
*
* @param newValue the new value
*/
public final void set(int newValue) {
value = newValue;
}
/**
* Eventually sets to the given value.
*
* @param newValue the new value
* @since 1.6
*/
public final void lazySet(int newValue) {
unsafe.putOrderedInt(this, valueOffset, newValue);
}
/**
* 原子性的设置新值,返回新值
*
* @param newValue the new value
* @return the previous value
*/
public final int getAndSet(int newValue) {
for (;;) {
int current = get();
if (compareAndSet(current, newValue))
return current;
}
}
/**
* 比较内存中的值和预期值,如果相同则更新,否则不进行操作
*
* @param expect the expected value
* @param update the new value
* @return true if successful. False return indicates that
* the actual value was not equal to the expected value.
*/
public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
/**
* Atomically sets the value to the given updated value
* if the current value {@code ==} the expected value.
*
* <p>May <a href="package-summary.html#Spurious">fail spuriously</a>
* and does not provide ordering guarantees, so is only rarely an
* appropriate alternative to {@code compareAndSet}.
*
* @param expect the expected value
* @param update the new value
* @return true if successful.
*/
public final boolean weakCompareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
/**
* 原子性的增加1,并返回当前值.
*
* @return the previous value
*/
public final int getAndIncrement() {
for (;;) {
int current = get();
int next = current + 1;
if (compareAndSet(current, next))
return current;
}
}
/**
* 原子性的减少1,并返回当前值.
*
* @return the previous value
*/
public final int getAndDecrement() {
for (;;) {
int current = get();
int next = current - 1;
if (compareAndSet(current, next))
return current;
}
}
/**
* 原子性的增加给定的值,并返回当前值.
*
* @param delta the value to add
* @return the previous value
*/
public final int getAndAdd(int delta) {
for (;;) {
int current = get();
int next = current + delta;
if (compareAndSet(current, next))
return current;
}
}
/**
* 原子性的增加1,并返回更新后的值.
*
* @return the updated value
*/
public final int incrementAndGet() {
for (;;) {
int current = get();
int next = current + 1;
if (compareAndSet(current, next))
return next;
}
}
/**
* 原子性的减少1,并放回更新后的值.
*
* @return the updated value
*/
public final int decrementAndGet() {
for (;;) {
int current = get();
int next = current - 1;
if (compareAndSet(current, next))
return next;
}
}
/**
* 原子性的增加给定的值,并返回更新后的值.
*
* @param delta the value to add
* @return the updated value
*/
public final int addAndGet(int delta) {
for (;;) {
int current = get();
int next = current + delta;
if (compareAndSet(current, next))
return next;
}
}
/**
* Returns the String representation of the current value.
* @return the String representation of the current value.
*/
public String toString() {
return Integer.toString(get());
}
public int intValue() {
return get();
}
public long longValue() {
return (long)get();
}
public float floatValue() {
return (float)get();
}
public double doubleValue() {
return (double)get();
}
}
CAS的缺点
- CAS的ABA问题
问题描述
- 线程p1在共享变量中读取到值A
- p1被抢占,进程p2执行
- p2把共享变量里的值从A改成了B,再改回到A,此时被p1抢占。
- p1回来看到共享变量里的值没有被改变,于是继续执行。
- 维基百科上的例子
你拿着一个装满钱的手提箱在飞机场,此时过来了一个火辣性感的美女,然后她很暖昧地挑逗着你,并趁你不注意的时候,把用一个一模一样的手提箱和你那装满钱的箱子调了个包,然后就离开了,你看到你的手提箱还在那,于是就提着手提箱去赶飞机去了
- CAS的ABA的解决办法
ABA问题的解决思路就是使用版本号。在变量前面追加上版本号,每次变量更新的时候把版本号加一
从Java1.5开始JDK的atomic包里提供了一个类AtomicStampedReference来解决ABA问题。这个类的compareAndSet方法作用是首先检查当前引用是否等于预期引用,并且当前标志是否等于预期标志,如果全部相等,则以原子方式将该引用和该标志的值设置为给定的更新值
- 循环时间长开销大
自旋CAS如果长时间不成功,会给CPU带来非常大的执行开销。如果JVM能支持处理器提供的pause指令那么效率会有一定的提升,pause指令有两个作用,第一它可以延迟流水线执行指令(de-pipeline),使CPU不会消耗过多的执行资源,延迟的时间取决于具体实现的版本,在一些处理器上延迟时间是零。第二它可以避免在退出循环的时候因内存顺序冲突(memory order violation)而引起CPU流水线被清空(CPU pipeline flush),从而提高CPU的执行效率
- 只能保证一个共享变量的原子操作
当对一个共享变量执行操作时,我们可以使用循环CAS的方式来保证原子操作,但是对多个共享变量操作时,循环CAS就无法保证操作的原子性,这个时候就可以用锁,或者有一个取巧的办法,就是把多个共享变量合并成一个共享变量来操作。比如有两个共享变量i=2,j=a,合并一下ij=2a,然后用CAS来操作ij。从Java1.5开始JDK提供了AtomicReference类来保证引用对象之间的原子性,你可以把多个变量放在一个对象里来进行CAS操作
并发编程CAS操作的更多相关文章
- 并发编程--CAS自旋锁
在前两篇博客中我们介绍了并发编程--volatile应用与原理和并发编程--synchronized的实现原理(二),接下来我们介绍一下CAS自旋锁相关的知识. 一.自旋锁提出的背景 由于在多处理器系 ...
- JAVA并发编程: CAS和AQS
版权声明:本文为博主原创文章,转载请注明出处 https://blog.csdn.net/u010862794/article/details/72892300 说起JAVA并发编程,就不得不聊 ...
- Java并发编程-CAS
CAS(Compare and swap)比较和替换是设计并发算法时用到的一种技术.简单来说,比较和替换是使用一个期望值和一个变量的当前值进行比较,如果当前变量的值与我们期望的值相等,就使用一个新值替 ...
- 【并发编程】【JDK源码】CAS与synchronized
线程安全 众所周知,Java是多线程的.但是,Java对多线程的支持其实是一把双刃剑.一旦涉及到多个线程操作共享资源的情况时,处理不好就可能产生线程安全问题.线程安全性可能是非常复杂的,在没有充足的同 ...
- 【并发编程】【JDK源码】J.U.C--AQS (AbstractQueuedSynchronizer)(1/2)
J.U.C实现基础 AQS.非阻塞数据结构和原子变量类(java.util.concurrent.atomic包中的类),concurrent包中的基础类都是使用这种模式来实现的.而concurren ...
- Java并发编程:Java线程池核心ThreadPoolExecutor的使用和原理分析
目录 引出线程池 Executor框架 ThreadPoolExecutor详解 构造函数 重要的变量 线程池执行流程 任务队列workQueue 任务拒绝策略 线程池的关闭 ThreadPoolEx ...
- Java并发编程实践
最近阅读了<Java并发编程实践>这本书,总结了一下几个相关的知识点. 线程安全 当多个线程访问某个类时,不管运行时环境采用何种调度方式或者这些线程将如何交替执行,并且在主调代码中不需要任 ...
- java并发编程(十七)Executor框架和线程池
转载请注明出处:http://blog.csdn.net/ns_code/article/details/17465497 Executor框架简介 在Java 5之后,并发编程引入了一堆新的启动 ...
- 转:【Java并发编程】之十九:并发新特性—Executor框架与线程池(含代码)
Executor框架简介 在Java5之后,并发编程引入了一堆新的启动.调度和管理线程的API.Executor框架便是Java 5中引入的,其内部使用了线程池机制,它在java.util.coc ...
随机推荐
- 在单片机上实现UDP
http://blog.chinaunix.net/uid-18921523-id-260999.html
- struts2学习(6)自定义拦截器-登录验证拦截器
需求:对登录进行验证,用户名cy 密码123456才能登录进去: 登录进去后,将用户存在session中: 其他链接要来访问(除了登录链接),首先验证是否登录,对这个进行拦截: com.cy.mod ...
- java代码--------编写0懂啊PI之间求随机数的方法
总结:其实每次运行,显示出来的结果个数是随机的. package com.mmm; //编写0到PI之间求随机数的方法 public class MEm { public static void ma ...
- 杂项:HTML5-3/3-技术要点
ylbtech-杂项:HTML5-3/3-技术要点 1.返回顶部 1. 重要标记 <video>标记 定义和用法: </video> 标签定义视频,比如电影片段或其他视频流 ...
- 安装Elastix-2.4版本
首先,下载Elastix地址:http://www.elastix.org,下载里面的2.4版本 第一步:选择安装,Enter 选择语言,默认就行 选择us,默认 选择全部 选择默认分区,点击OK 配 ...
- GITBOOK/HEXO TRAVIS GITHUB-PAGES 博客搭建
简介 这年头要是没有个博客都不好意思给别人说你是程序员,我用XX笔记呀,不行吗?不行,这玩意儿要么不能公开分享,要么公开分享要会员,现在到处都是开源,自己学到了东西都不能分享给需要帮助的人,真是伤心呀 ...
- ALSA声卡笔记1---ALSA驱动框架
1.声卡驱动程序sound.c (1)入口函数里通过register_chrdev()函数注册file_operations 结构体 (2)file_operations 结构体,里面只有open函数 ...
- 关于Struts2配置文件名修改的问题
突发奇想的想知道Struts2的配置文件名是否可以修改,自己最早使用Struts2的时候,只是单纯的将配置文件命名为“struts.xml”,这是Strut2默认的配置文件名,我一直也没有去修改它的命 ...
- SQL DATE_SUB 函数用法
SQL DATE_SUB 是一个mySql函数.不象SQL DATE_ADD 函数 增加时间值,SQL DATE_SUB 将从一个日期/时间值中减去一个时间值(时间间隔). SQL DATE_SUB ...
- sql查询语句常用例子
1.查找与jams在同一个单位的员工姓名.性别.部门和职称:select emp_no, emp_name, dept, title from employee where emp_name< ...