在atomic包中,大多数类都是借助unsafe类来实现的,如以下代码

    public static AtomicInteger count = new AtomicInteger(0);

    private  static void add() {
count.incrementAndGet();
}

incrementAndGet()方法的实现如下:

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

我们再继续深入getAndInt()方法,实现如下:

    public final int getAndAddInt(Object var1, long var2, int var4) {
int var5;
do {
var5 = this.getIntVolatile(var1, var2);
} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4)); return var5;
}

在以上代码中我们着重要说的是 compareAndSwapInt(var1, var2, var5, var5 + var4) 这个方法,compareAndSwap,取每个单词首写字母,就是我们经常说的cas。这个方法中有四个参数var1为当前对象,即代码中的count,var2为当前值,如想计算2加上1等于3的操作,var2即为2,var4为增加量,也就是这个例子中的1,var5为调用底层方法得到的底层当前的值,如果没有其他线程改变底层当前值,返回为2,compareAndSwapInt方法的作用为,如果var2(当前值)与var5(底层当前值相等)则将底层值覆盖为底层当前值(var5)+增加量(var4),否则(其他线程更改了底层当前值,var5不等于var2),重新从底层方法取一次var5的值,如此时var5=4,并重新从var1(当前对象)取一次var2的值,如此时var2的值也变为4,则采取相加操作覆盖底层值,如果var2与var5仍不等,则继续循环取值,直到相等为止。

总结一下,CAS操作包含三个操作数,内存位置(V),预期原值(A)和新值(B),如果内存位置的值与预期原值相匹配,处理器会自动将该位置值更新为新值,否则处理器不做任何操作。CAS指令一般都返回该位置的值(有特殊情况只返回是否成功),CAS简而言之就是,我认为位置V应该包含值A,如果包含该值,就将B放到这个位置,否则不更改该位置的值,只告诉我这个位置现在的值即可 。

对于getIntVolatile方法和compareAndSwapInt的实现如下:

public native int getIntVolatile(Object var1, long var2);
public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);

可将两个方法都由native修饰,native修饰的方法为底层方法,一般由c语言来实现。

锁分为悲观锁和乐观锁,独占锁是一种悲观锁,synchronized是一种独占锁,如果锁被占用,其他需要锁的线程就会被挂起,直到持有锁的线程释放锁,它会产生的问题如下:

a、在多线程竞争下,加锁释放锁会导致比较多的上下文切换和调度延时,引起性能问题

b、如果一个优先级高的线程等待一个优先级低的线程释放锁会导致优先级倒置,引起性能风险

另一种就是乐观锁,它每次执行时并不加锁而是假设没有冲突的去完成操作,如果因为冲突失败就重试,直到成功为止,而客观锁用到的机制就是CAS。虽然CAS可以很高效的解决原子操作,但是CAS仍然存在三大问题:

a、ABA问题,如果一个值原来是A,后被改成B,之后又改为A,那么使用CAS进行检查时发现它的值并没有发生变化,但是实际上却发生变化了,ABA问题的解决思路就是使用版本号,在变量面前加上版本号,每次变量更新的时候把版本加一,上面博客中我们提到atomic包中有专门的类来解决ABA问题

b、循环时间长开销大。CAS循环如果长时间不成功,会给CPU带来非常大的执行开销。如果JVM能支持处理器提供的pause指令则效率会有一定的提升,pause指令有两个作用,推迟流水线执行,使cpu不会消耗过多的执行资源和避免退出循环的时候因内存顺序冲突而引起CPU流水线被清空,提高CPU执行效率

c、只能保证一个共享变量的原子操作。当对一个共享变量执行操作时,我们可以使用循环CAS来保证原子操作,但是对多个共享变量操作时,循环CAS就无法保证操作的原子性,这个时候可以用锁或把多个共享变量合并成一个共享变量来操作或者把多个变量放在一个对象里(atomic包中有对引用类型的原子操作)来进行CAS操作

并发编程(五):CAS的更多相关文章

  1. 【Java并发编程五】信号量

    一.概述 技术信号量用来控制能够同时访问某特定资源的活动的数量,或者同时执行某一给定操作的数据.计数信号量可以用来实现资源池或者给一个容器限定边界. 信号量维护了一个许可集,许可的初始量通过构造函数传 ...

  2. java并发编程(八) CAS & Unsafe & atomic

    参考文档:https://www.cnblogs.com/xrq730/p/4976007.html CAS(Compare and Swap) 一个CAS方法包含三个参数CAS(V,E,N).V表示 ...

  3. Java并发编程 (五) 线程安全性

    个人博客网:https://wushaopei.github.io/    (你想要这里多有) 一.安全发布对象-发布与逸出 1.发布与逸出定义 发布对象 : 使一个对象能够被当前范围之外的代码所使用 ...

  4. 聊聊CAS - 面试官最喜欢问的并发编程专题

    什么是CAS 学习Java并发编程,CAS(Compare And Set)机制都是一个不得不掌握的知识点.除了通过synchronized进行并发控制外,还可以通过CAS的方式控制,大家熟悉的Ree ...

  5. Java并发(一)并发编程的挑战

    目录 一.上下文切换 1. 多线程一定快吗 2. 测试上下文切换次数和时长 3. 如何减少上下文切换 4. 减少上下文切换实战 二.死锁 三.资源限制的挑战 四.本章小结 并发编程的目的是为了让程序运 ...

  6. java(9)并发编程

    整理自<java 并发编程的艺术> 1. 上下文切换 即使是单核处理器也支持多线程执行代码,CPU通过给每个线程分配CPU时间片来实现这个机制.时间片是CPU分配给各个线程的时间,因为时间 ...

  7. 读《Java并发编程的艺术》(一)

    离开博客园很久了,自从找到工作,到现在基本没有再写过博客了.在大学培养起来的写博客的习惯在慢慢的消失殆尽,感觉汗颜.所以现在要开始重新培养起这个习惯,定期写博客不仅是对自己学习知识的一种沉淀,更是在督 ...

  8. java并发编程的艺术——第一章总结

    并发编程的挑战 1.1上下文切换 1.2死锁 1.3资源限制的挑战 1.4本章小结 1.1上下文切换 1.1.1多线程一定快吗 1.1.2测试上下文切换次数和时长 1.1.3如何减少上下文切换 1.1 ...

  9. 《Jave并发编程的艺术》学习笔记(1-2章)

    Jave并发的艺术 并发编程的挑战 上下文切换 CPU通过时间片分配算法来循环执行任务,当前时间片执行完之后会切换到下一个任务.但是,切换会保存上一个任务的状态,一遍下次切换回这个任务时,可以再次加载 ...

  10. Java并发编程的挑战

    并发编程的目的是为了让程序运行得更快,但是,并不是线程启动的越多,就能让程序最大限度地并发执行.并发编程时,会面临非常多的挑战,比如上下文切换的问题,死锁的问题,以及受限于各种硬件和软件的资源限制问题 ...

随机推荐

  1. 解决uploadify在使用IE内核等浏览器无法使用

    有两种方法: 第一种: SWFUpload Version: 2.2.0 Beta 2 Flash Player Version: current Operating System:Window 7 ...

  2. Android学习之Animation(二)

    接着上次的View Animation动画,这次是Frame Animation.具体点来讲就是在Frame层面上进行变化的动画效果的设置.说白了就是定时更换"背景"图.来实现不同 ...

  3. PA模块常用表

    SELECT * FROM pa_projects_all; --项目 SELECT * FROM pa_project_types; --项目类型 SELECT * FROM pa_project_ ...

  4. JSP编译成Servlet(三)JSP编译后的Servlet

    JSP编译后的Servlet类会是怎样的呢?他们之间有着什么样的映射关系?在探讨JSP与Servlet之间的关系时先看一个简单的HelloWorld.jsp编译成HelloWorld.java后会是什 ...

  5. Gradle脚本打包so库

    要让引用的第三方的so库被打包进去,只需要把相关的armeabi文件夹放在libs下面,然后在builld.gradle脚本中加上这一句: sourceSets{ main { jniLibs.src ...

  6. java的制作"时间账本"

    一直以来我都感觉自己的时间过得好荒废啊,貌似只是打开了一个网页链接的时间,一个下午便过去了:仿佛就是看了看空间,刷了刷微信,一天就过去了.哈,当然这是夸张的说法.但是我仔细地算了一下,大概我们每个人每 ...

  7. Python学习笔记 - ifelifelse-forin-while

    if elif else #!/usr/bin/env python3 # -*- coding: utf-8 -*- age = 20 if age >= 18: print('your ag ...

  8. Dynamics CRM 同一实体多个Form显示不同的Ribbon按钮

    自CRM2011引入多FORM窗体,并且对不同的窗体引入了角色控制,给我们的客制化开发带来了多样化,既然有了多窗体也就理所当然的有了在不同的窗体显示不同的Ribbon按钮的需求,具体怎么做见下面的博客 ...

  9. Cocos2D v2.0至v3.x简洁转换指南(三)

    Cocos2D 3.3中的注意事项 如果你在使用Cocos2D 3.3+(是SpriteBuilder 1.3+的一部分)你将不得不替分别的换所有存在的UITouch和UITouchEvent为CCT ...

  10. Android进阶(十九)AndroidAPP开发问题汇总(三)

    Android进阶(十九)AndroidAPP开发问题汇总(三) Java解析XML的几种方式: http://inotgaoshou.iteye.com/blog/1012188 从线程返回数据的两 ...