CAS算法即是:Compare And Swap,比较并且替换;
    CAS算法存在着三个参数,内存值V,旧的预期值A,以及要更新的值B。当且仅当内存值V和预期值B相等的时候,才会将内存值修改为B,否则什么也不做,直接返回false;
    比如说某一个线程要修改某个字段的值,当这个值初始化的时候会在内存中完成,根据Java内存模型,该线程保存着这个变量的一个副本;当且仅当这个变量的副本和内存的值如果相同,那么就可以完成对值得修改,并且这个CAS操作完全是原子性的操作,也就是说此时这个操作不可能被中断。
    先来看一个n++的问题:

public class Case {
public volatile int n; public void add() {
n++;
}
}

上述代码中什么变量被volatile修饰,此时说明该变量在多线程操作的情况下可以保证内存的可见性,但是不可以保证原子性操作,因此在多线程并发的时候还是会出现问题的;利用Javap命令来看看汇编指令:

PS D:\ssh> javac Case.java
PS D:\ssh> javap -c Case
Compiled from "Case.java"
public class Case {
public volatile int n; public Case();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return public void add();
Code:
0: aload_0
1: dup
2: getfield #2 // Field n:I
5: iconst_1
6: iadd
7: putfield #2 // Field n:I
10: return
}
PS D:\ssh>

  

在方法add()中,第17行表示获取到了n的初始值;
                          第19行执行了iadd()操作,n加一;
                          第20行执行了putfield,把新累加的值赋值给n;
在上面我很清楚的说过volatile确实无法保证上述三个操作步骤的原子性;可以使用synchrnoized的方法完成原子性的操作;synchrnoized是互斥锁,也是可重入的锁,可以保证操作的原子性;但是加锁之后效率降低,
    好了,接下来再看一段代码:

public int a = 1;
public boolean compareAndSwapInt(int b) {
   if (a == 1) {
       a = b;
       return true;
  }
   return false;
}
上述方法在并发的情况下也是会出现问题的;当多个线程直接进入compareAndSwapInt()之后,他们也同时符合上述的逻辑判断,此时对a的赋值也有可能同事发生,这样也带来了线程安全的问题;
同样加锁的方式也可以解决这个问题,但是在这里我们不研究锁的问题;下面我们来看看一段代码,这是AtomicInteger类中的一部分源码:
public class AtomicInteger extends Number implements java.io.Serializable {
private static final long serialVersionUID = 6214790243416807050L; // setup to use Unsafe.compareAndSwapInt for updates
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); }
} private volatile int value; /**
* Gets the current value.
*
* @return the current value
*/
public final int get() {
return value;
}
}
1 Unasfe是CAS的核心类,通过这个类可以获取字段在内存中的地址偏移量;Unsafe是native的,我们一般不可能使用;这是Java对硬件操作的支持;
2 valueOffset是地址偏移量(变量在内存中的地址偏移量)
3 value是使用volatile修饰的,保证了内存的可见性;
    平时做常用的方法addAndGet()方法;作用是原子性的操作给变量添加值;
int addAndGet(int delta)           以原子方式将给定值与当前值相加。

在Java8中,这个方法的实现是调用了unsafe()方法;因此我们看不到;

 public final int addAndGet(int delta) {
return unsafe.getAndAddInt(this, valueOffset, delta) + delta;
}

但是通过网上看到了该方法的实现方式:

public final int addAndGet(int delta) {
for (;;) {
int current = get();
int next = current + delta;
if (compareAndSet(current, next))
return next;
}
}
  public final int get() {
return value;
}
假设delta的值为1,在CAS算法下操作的话,首先进入一个for循环体;假设存在着两个线程,并且内存中的值value=3;根据Java内存模型,每一个线程都存在这这个变量的副本;
    1) 线程1进入循环体,获取到current的值为3,然后获取到到next的值此时为4;此时假设线程1运气不好,被挂起;
    2)线程2进入循环体,获取到current的值为3,同时next的值也为4;线程2运气好,此时继续执行compareAndSet()方法;

public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
    线程2传入两个参数,一个当前值,以及一个预期值;当前值,也就是current=3.要修改成为4;此时当前值也就是预期值和内存中的value比较,此时都是3,那么修改内存中的值为4;
    3)线程1此时再次执行compareAndSwapInt()方法的时候。发现内存中的值为4,预期的值是3,两者不相等,此时就不可以再次赋值了;
 
CAS的缺点:
    CAS存在和“ABA的漏洞”;什么是ABA呢?
    假定在某个时刻某个线程从内存中取出A,然后在下个时刻准备更新这个值;在这个时间差内数据发生了改变;

 假设线程1从内存中取出了A,线程2也从内存中取出了A,并且将值修改为B,最后又改为A,当线程1去更新值得时候发现内存中的数据和线程备份数据相同,可以更新;但是此时内存中的值其实发生了变化的,只不过又变回去了;在实际的开发过程中,ABA可能会带来一些问题,但是我认为无关紧要,我们需要的只是数值的变化而已;
    对于单向链表实现的栈而言;假设存在一个链表  A---->B;线程1要去将栈顶的数据修改为B,但是此时线程2进来之后,A---->B出栈,D、C、A压栈;此时链表的结构发生了变化;A---->C---->D;此时线程1发现栈顶元素还是A,而元素B被出栈之后成为一个游离的对象,
    解决方式:由于CAS算法没有直接的使用锁;而是通过乐观锁的方式去控制并发的;而对于乐观锁而言一般都是操作+时间戳来控制每一次的版本号的;在JDK类库中,可以使用AutomicStampReference来解决

并发之CAS无锁技术的更多相关文章

  1. CAS无锁技术

    前言:关于同步,很多人都知道synchronized,Reentrantlock等加锁技术,这种方式也很好理解,是在线程访问的临界区资源上建立一个阻塞机制,需要线程等待 其它线程释放了锁,它才能运行. ...

  2. 探索CAS无锁技术

    前言:关于同步,很多人都知道synchronized,Reentrantlock等加锁技术,这种方式也很好理解,是在线程访问的临界区资源上建立一个阻塞机制,需要线程等待 其它线程释放了锁,它才能运行. ...

  3. java并发:AtomicInteger 以及CAS无锁算法【转载】

    1 AtomicInteger解析 众所周知,在多线程并发的情况下,对于成员变量,可能是线程不安全的: 一个很简单的例子,假设我存在两个线程,让一个整数自增1000次,那么最终的值应该是1000:但是 ...

  4. (转载)java高并发:CAS无锁原理及广泛应用

    java高并发:CAS无锁原理及广泛应用   版权声明:本文为博主原创文章,未经博主允许不得转载,转载请注明出处. 博主博客地址是 http://blog.csdn.net/liubenlong007 ...

  5. CAS无锁算法与ConcurrentLinkedQueue

    CAS:Compare and Swap 比较并交换 java.util.concurrent包完全建立在CAS之上的,没有CAS就没有并发包.并发包借助了CAS无锁算法实现了区别于synchroni ...

  6. CAS无锁机制原理

    原子类 java.util.concurrent.atomic包:原子类的小工具包,支持在单个变量上解除锁的线程安全编程 原子变量类相当于一种泛化的 volatile 变量,能够支持原子的和有条件的读 ...

  7. CAS无锁实现原理以及ABA问题

    CAS(比较与交换,Compare and swap) 是一种有名的无锁算法.无锁编程,即不使用锁的情况下实现多线程之间的变量同步,也就是在没有线程被阻塞的情况下实现变量的同步,所以也叫非阻塞同步(N ...

  8. CAS无锁操作

    https://coolshell.cn/articles/8239.html 主要讲的是<Implementing Lock-Free Queues>的论点,具体直接看论文最好.这里总结 ...

  9. CAS无锁策略

    并发编程时,对于共享资源的使用需要确保绝对的安全性.除了利用锁机制之外,还有一种无锁的概念.所谓无锁,就是假定在并发情况下,对于共享资源的访问没有冲突,线程可以一直不停的运行,无需阻塞,如果产生冲突, ...

随机推荐

  1. WebLogic发布S2SH应用时提示ClassNotFoundException: org.hibernate.hql.ast.HqlToken异常

    使用Spring+hibernate如下 <properties> <!--定义方言.fetch深度.是否显示sql--> <property name="hi ...

  2. PostgreSQL查询长连接

    apple=# select pid, backend_start, xact_start, query_start, waiting, state, backend_xid from pg_stat ...

  3. HDU 1358

    http://acm.hdu.edu.cn/showproblem.php?pid=1358 求某个前缀的周期,用Next求循环节的题目 #include <iostream> #incl ...

  4. CF1143C Queen

    CF1143C Queen 开始想大力维护 \(bfs\) 序+数据结构解决,但 \(bfs\) 序的变化不太好推. 换了一个思路,注意到删除一个点后,原来可以被删除的点仍然可以被删除,原来不能被删除 ...

  5. Session History 属性和方法

    History 接口允许操作浏览器的曾经在标签页或者框架里访问的会话历史记录. js通过window.history来访问和操作的,操作的范围是某个tab的会话历史记录. 这个tab打开后,tab内的 ...

  6. 6-19 Count Connected Components(20 分)

    Write a function to count the number of connected components in a given graph. Format of functions: ...

  7. wpf 客户端【JDAgent桌面助手】开发详解-开篇

    上周二 发表了一个帖子:wpf 客户端[JDAgent桌面助手]业余开发的终于完工了..晒晒截图... 没有想到不到一个周时间已经浏览量过8千,估计过几天就过万了..很开心啊.. 评论中好多网友问我要 ...

  8. centeros php 实战

    apache 默认安装路径 Fedora Core, CentOS, RHEL:ServerRoot              ::      /etc/httpdPrimary Config Fle ...

  9. docker默认配置文件不存在问题解决

    Docker默认的配置文件/etc/default/docker或者/etc/sysconfig/docker都不起作用,查看了一下/lib/systemd/system/docker.service ...

  10. bzoj1013球形空间

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1013 根据距离式子,两个点的话,两边平方再消掉x^2之后有: a1^2 - 2*a1*x1 ...