并发编程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 ...
随机推荐
- RTMP(实时信息传输协议)详解
RTMP协议是Real Time Message Protocol(实时信息传输协议)的缩写,它是由Adobe公司提出的一种应用层的协议,用来解决多媒体数据传输流的多路复用(Multiplexing) ...
- Java-Runoob:Java Character 类
ylbtech-Java-Runoob:Java Character 类 1.返回顶部 1. Java Character 类 Character 类用于对单个字符进行操作. Character 类在 ...
- jQuery笔记——插件
验证插件 验证插件(validate.js),是一款验证常规表单数据合法性的插件.使用它,极大的解 放了在表单上繁杂的验证过程,并且错误提示显示的完善也增加了用户体验 插件都可以在JQueryUI 的 ...
- 【BZOJ】1001: [BeiJing2006]狼抓兔子(最小割 / 对偶图)
题目 传送门:QWQ 分析 显然答案是最小割. 然后dinic卡一卡过去了. 其实是懒得写转对偶图:正解 (dinic原来写的是vector,后来改的比较鬼畜 代码 #include <bits ...
- C++ 栈 (链表实现)
第一.基本概念 栈中的元素遵守“先进后出”的原则(LIFO,Last In First Out) 只能在栈顶进行插入和删除操作 压栈(或推入.进栈)即push,将数据放入栈顶并将栈顶指针加一 出栈(或 ...
- Java web概述
声明:为了使初学者能更好的了解java web,所以我整理了下资料,概述了web应用程序. 一.web概述 静态web资源:内容是静态的,不同的人在不同的时间来访问时都是相同的内容.HTML.CSS. ...
- Linux 为FTP 服务器添加iptables规则--案例分析
一.故障描述 由于开发提出需求,为他们搭建内部ftp服务器,搭建好后,提交给他们,测试可以正常使用.后来过了一段时间后,有一天无法登陆了.于是去ftp主机上检查问题,ftp的配置文件没有改动,端口监听 ...
- 深入浅出 Java Concurrency (12): 锁机制 part 7 信号量(Semaphore)
Semaphore 是一个计数信号量.从概念上讲,信号量维护了一个许可集.如有必要,在许可可用前会阻塞每一个 acquire(),然后再获取该许可.每个 release() 添加一个许可,从而可能 ...
- 黑暗之光 Day1
1. 设置鼠标指针图标 Build Setting -> Player Setting 2. 添加雾的效果 Window->Lighting->Settings. 3. 任意键按下事 ...
- Java 单例模式详解(转)
概念: java中单例模式是一种常见的设计模式,单例模式分三种:懒汉式单例.饿汉式单例.登记式单例三种. 单例模式有一下特点: 1.单例类只能有一个实例. 2.单例类必须自己自己创建自己的唯一实例. ...