并发编程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操作的更多相关文章

  1. 并发编程--CAS自旋锁

    在前两篇博客中我们介绍了并发编程--volatile应用与原理和并发编程--synchronized的实现原理(二),接下来我们介绍一下CAS自旋锁相关的知识. 一.自旋锁提出的背景 由于在多处理器系 ...

  2. JAVA并发编程: CAS和AQS

       版权声明:本文为博主原创文章,转载请注明出处 https://blog.csdn.net/u010862794/article/details/72892300 说起JAVA并发编程,就不得不聊 ...

  3. Java并发编程-CAS

    CAS(Compare and swap)比较和替换是设计并发算法时用到的一种技术.简单来说,比较和替换是使用一个期望值和一个变量的当前值进行比较,如果当前变量的值与我们期望的值相等,就使用一个新值替 ...

  4. 【并发编程】【JDK源码】CAS与synchronized

    线程安全 众所周知,Java是多线程的.但是,Java对多线程的支持其实是一把双刃剑.一旦涉及到多个线程操作共享资源的情况时,处理不好就可能产生线程安全问题.线程安全性可能是非常复杂的,在没有充足的同 ...

  5. 【并发编程】【JDK源码】J.U.C--AQS (AbstractQueuedSynchronizer)(1/2)

    J.U.C实现基础 AQS.非阻塞数据结构和原子变量类(java.util.concurrent.atomic包中的类),concurrent包中的基础类都是使用这种模式来实现的.而concurren ...

  6. Java并发编程:Java线程池核心ThreadPoolExecutor的使用和原理分析

    目录 引出线程池 Executor框架 ThreadPoolExecutor详解 构造函数 重要的变量 线程池执行流程 任务队列workQueue 任务拒绝策略 线程池的关闭 ThreadPoolEx ...

  7. Java并发编程实践

    最近阅读了<Java并发编程实践>这本书,总结了一下几个相关的知识点. 线程安全 当多个线程访问某个类时,不管运行时环境采用何种调度方式或者这些线程将如何交替执行,并且在主调代码中不需要任 ...

  8. java并发编程(十七)Executor框架和线程池

    转载请注明出处:http://blog.csdn.net/ns_code/article/details/17465497   Executor框架简介 在Java 5之后,并发编程引入了一堆新的启动 ...

  9. 转:【Java并发编程】之十九:并发新特性—Executor框架与线程池(含代码)

      Executor框架简介 在Java5之后,并发编程引入了一堆新的启动.调度和管理线程的API.Executor框架便是Java 5中引入的,其内部使用了线程池机制,它在java.util.coc ...

随机推荐

  1. SQL语句 合并列值 将一列的多个值合并成一行

    效果: oralce写法: select WM_CONCAT(A.title) as citys from tmpcity A sql server写法: select stuff((select ' ...

  2. java代码--------实现位运算符不用乘除法啊

    总结:<<:乘法 >>:除法 package com.mmm; public class dfd { public static void main(String[] args ...

  3. excel打开csv 出现乱码怎么解决

    CSV是逗号分隔值的英文缩写,通常都是纯文本文件.CSV格式是分隔的数据格式,有字段/列分隔的逗号字符和记录/行分隔换行符.通常CSV文件可以用EXCEL正常打开,但是许多人都有这样的经历,使用EXC ...

  4. Tkinter学习

    from tkinter import * window = Tk() # 创建一个窗口 window.mainloop() # 消息循环,显示窗口 window.title("窗口标题&q ...

  5. Timestamp类型浅析

    Oracle针对不同的数据需求,提供了多种类.多层次的数据类型体系.我们在实际应用中,最好可以依据业务数据的实际形态和前端应用的语言.框架特性来确定字段类型的选择. Date类型是我们经常使用的时间类 ...

  6. SharePoint 事件 7363:对象缓存:缓存使用的超级读者帐户没有足够的权限访问SharePoint数据库。

    转自MSND:http://technet.microsoft.com/zh-cn/library/ff758656(v=office.14) 对象缓存存储 Microsoft SharePoint ...

  7. ce

    一,什么是epel 如果既想获得 RHEL 的高质量.高性能.高可靠性,又需要方便易用(关键是免费)的软件包更新功能,那么 Fedora Project 推出的 EPEL(Extra Packages ...

  8. Novell云计算

    老牌操作系统NetWare,淡出人们的视野,已记不清是哪一年的事了.不过,它的拥有者-NOVELL公司,却没有像自己曾经红极一时的OS那样被人遗忘.相反,在今天云计算的大潮中,Novell华丽转身,摇 ...

  9. Django 的分页器

    from  不能发json格式   只能发四种格式 view from django.shortcuts import render,HttpResponse # Create your views ...

  10. dart 公共变量

    dart中可以直接在一个文件里声明一个变量,这在其他语言中并不常见,比如c#语言只有类型才可以在命名空间下定义,变量必须放在类里声明 所以dart这点特性类似于js 今天就来讨论这个公共变量的作用范围 ...