CAS:简单的来说就是比较交换!那么比较的是什么?交换的又是什么呢?
CAS有三个操作数,V,A,B。要比较的就是V和A,当V和A相等的时候,就将V的值更新为B.
感觉就像“天王盖地虎”对“小鸡炖蘑菇”一样,暗号对上了(V==A)就可以进行下一步的操作(更新)了
上面这段描述可以简单的伪代码表示为:

int value;
public synchronized int compareAndSwap(int A,int B){int V = value;if(V==A){value=B;}return V;
}

当且仅当V和A匹配成功时,才会将value替换成新的值。也就是说我认为V的值应该是A,如果是,那么将V的值更新为B。注意无论如何,我们返回的都是V原有的值。
所以compareAndSet可以这么写:

//如果是true的话,说明值更新成功,否则则是更新失败。
public synchronized int compareAndSet(int A,int B){return (A==compareAndSwap(A,B);
}

简单举个例子:

1、首先定义一个类,名为CAS,该类实现了Runnable,为CAS类提供V、A、B三个变量

 private AtomicBoolean V = new AtomicBoolean(true);private boolean A = true;private boolean B = false;

2、为CAS类设计两个方法在lock方法模拟自旋,unlock方法将V值复位:

 //模拟自旋private void lock() {while (!V.compareAndSet(A, B)) {// do nothingprintln(Thread.currentThread().getName() + "--lock--进入自旋转-");// 加一个sleep操作,防止while循环过快执行sleep(1000);}}//V值复位为falseprivate void unlock() {V.compareAndSet(B, A);println(Thread.currentThread().getName() + "----unlock-V-" + V.get());}public void sleep(long time) {try {Thread.sleep(time);} catch (Exception e) {}}

看lock方法其实很简单,就是一个while循环不断比较!V.compareAndSet(A, B),为了防止调用速度过快,博主可以sleep了一秒钟。
3、在Runnable的run方法中这么调用:

public void run() {lock();println(Thread.currentThread().getName() + " 开始工作,此时V的值修改为==" + V.get());sleep(5000);unlock();}

4、开启两个Thread执行CAS:

public static void main(String args[]) {CAS cas = new CAS();Thread a = new Thread(cas);a.setName("线程A");Thread b = new Thread(cas);b.setName("线程B");a.start();b.start();}

5、执行结果就是:

线程A 开始工作,此时V的值修改为==false
线程B--lock--进入自旋转-
线程B--lock--进入自旋转-
线程B--lock--进入自旋转-
线程B--lock--进入自旋转-
线程B--lock--进入自旋转-
线程B--lock--进入自旋转-
线程A----unlock-V-true
线程B 开始工作,此时V的值修改为==false
线程B----unlock-V-true

从上面的打印可以看出CAS对于两个线程来说,都没有挂起;都在各自执行自己的东西,比如A线程执行自己的打印,B线程就不断的执行cas自旋转。所以CAS的也成为无锁编程,即不使用锁的情况下实现多线程之间的变量同步,也就是在没有线程被阻塞的情况下实现变量的同步,所以也叫非阻塞同步,所以可以用CAS来模拟实现乐观锁,为了区别,这里可以简单的写一个demo,使用sun.misc.Lock来改写我们的run方法:

  //定义一把锁
// 定义一把锁private Lock lock = new Lock();public void run() {// 加锁try {lock.lock();PrintUtils.println(Thread.currentThread().getName() + "-获取到了锁--");} catch (InterruptedException e) {}PrintUtils.println(Thread.currentThread().getName() + " 开始工作五秒钟。。。。。");ThreadUtils.sleep(5000);PrintUtils.println(Thread.currentThread().getName() + "工作完毕,释放锁-");lock.unlock();}

继续执行上面的main方法,运行效果如下(可以看出A获取锁的时候,B就阻塞住了):

线程A-获取到了锁--
线程A 开始工作五秒钟。。。。。
线程A工作完毕,释放锁-
线程B-获取到了锁--
线程B 开始工作五秒钟。。。。。
线程B工作完毕,释放锁-

对于上面的CAS的代码,如果不用lock和unlock的话,我们也可以把run方法改写成如下所示:

public void run() {if (V.compareAndSet(A, B)) {// 此时当前线程符合要求println(Thread.currentThread().getName() + " 开始工作,此时V修改为==" + V.get());sleep(5000);     V.set(A);println(Thread.currentThread().getName() + "unlock-V-"+V.get());} else {println(Thread.currentThread().getName() + "--lock--进入自旋转-");// 防止StackOverflowErrorsleep(1000);//invoke self,模拟CASrun();}}

需要注意到是else分支调用run方法自己的时候,之所以sleep(1000),是为了防止调用过快,造成StackOverflowError的错误!


当然还有一个更优雅的自旋实现方法,用AtomicReference这个类!代码如下:

 private AtomicReference<Thread> owner = new AtomicReference<Thread>();@Overridepublic void run() {lock();PrintUtils.println(Thread.currentThread().getName() + " 开始工作五秒钟....");ThreadUtils.sleep(5000);unlock();}//模拟自旋private void lock() {Thread current = Thread.currentThread();while(!owner.compareAndSet(null, current)) {PrintUtils.println(current + " 开始自旋" );ThreadUtils.sleep(1000);}}private void unlock() {Thread current = Thread.currentThread();PrintUtils.println(current.getName() + "工作完毕");owner.compareAndSet(current, null);}

运行效果如下:

线程A 开始工作五秒钟....
Thread[线程B,5,main] 开始自旋
Thread[线程B,5,main] 开始自旋
Thread[线程B,5,main] 开始自旋
Thread[线程B,5,main] 开始自旋
Thread[线程B,5,main] 开始自旋
Thread[线程B,5,main] 开始自旋
线程A 工作完毕
线程B 开始工作五秒钟....
线程B 工作完毕

自旋转锁保证了在不阻塞的情况下对共享资源的互斥访问,自旋锁不会使线程状态发生切换,一直处于用户态,即线程一直都是active的;不会使线程进入阻塞状态,减少了不必要的上下文切换,执行速度快。但是其缺乏公平性。为什么这么说呢?考虑一个现实的场景,博主高中的时候去学校食堂吃饭,在一个小窗口打饭的时候,刚开始几乎没人排队,然后大家一拥而上,跟打仗似的;但是就一个打菜的阿姨(共享资源),当阿姨跟一个人打菜的时候,后面的就得等着;但是当前一个人打完之后,后面的谁劲大,谁挤到前面就有优先打到菜的好处,根本没有什么先来后到这一说。这就不公平了。同样的当一个自旋锁被使用期间,别的需要该锁CPU就需要等待,一旦锁被释放,这些等待的CPU谁会首先获取锁呢?那就不得而知了,有可能最先请求锁的那个CPU最后获得锁。

那么怎么解决这个问题呢?排队自旋锁应运而生,就像后来博主的高中食堂开始排队打菜一样,具体什么是排队自旋锁,后面会继续说明
关于CAS,就简单的介绍到这儿,如有不当之处欢迎批评指正,共同学习和提高。博客demo代码传送门

java线程知识点拾遗(CAS)相关推荐

  1. Java线程专栏文章汇总(转)

    原文:http://blog.csdn.net/ghsau/article/details/17609747 JDK5.0之前传统线程        Java线程(一):线程安全与不安全 Java线程 ...

  2. 一文弄懂Java线程安全队列

    文章目录 一.分类 二.BlockingQueue 阻塞队列 三.ConcurrentLinkedQueue 非阻塞队列 一.分类 java中所有队列都继承至java.util.Queue接口,该接口 ...

  3. Java核心知识点 --- 线程中如何创建锁和使用锁 Lock , 设计一个缓存系统

    理论知识很枯燥,但这些都是基本功,学完可能会忘,但等用的时候,会发觉之前的学习是非常有意义的,学习线程就是这样子的. 1.如何创建锁? Lock lock = new ReentrantLock(); ...

  4. 干货分享 JVM 之第 1 篇 —— Java 线程的重要知识点大全

    线程与进程区别 每个正在系统上运行的程序都是一个进程.每个进程包含一到多个线程.线程是一组指令的集合,或者是程序的特殊段,它可以在程序里独立执行.也可以把它理解为代码运行的上下文.所以线程基本上是轻量 ...

  5. 【韩顺平】Java线程(基础)知识点总结

    文章目录 1.线程概念 1.1程序 1.2进程 1.3线程 2.其他相关概念 2.1并发 2.2并行 2.3单线程 2.4多线程 3.创建线程的两种方法 3.1继承Thread类,重写run方法 3. ...

  6. Java 面试知识点解析(三)——JVM篇

    前言: 在遨游了一番 Java Web 的世界之后,发现了自己的一些缺失,所以就着一篇深度好文:知名互联网公司校招 Java 开发岗面试知识点解析 ,来好好的对 Java 知识点进行复习和学习一番,大 ...

  7. Java面试知识点:多线程

    问题:Java面试知识点:多线程 答案: 1.线程 代码如下: package com.xy;/*** @ProjectName: day01* @Package: com.xy* @ClassNam ...

  8. java线程池_Java 并发编程 线程池源码实战

    作者 | 马启航 杏仁后端工程师.「我头发还多,你们呢?」 一.概述 笔者在网上看了好多的关于线程池原理.源码分析相关的文章,但是说实话,没有一篇让我觉得读完之后豁然开朗,完完全全的明白线程池,要么写 ...

  9. 字节大牛教你手撕Java学习,Java核心知识点

    线程 线程的启动 实现Runnab1e接口 继承Thread类 实现Callable接口 线程的状态 线程的方法 线程的优先级 守护线程 未捕获异常处理器 并发编程的问题 线程引入开销:上下文切换与内 ...

  10. 最全最新的的Java核心知识点整理!!! 【推荐】

    前言: 想要文档版的小伙伴们可以私信我领取哦,更加清晰 一目了然 ~ Java核心知识点! 博客整理出来的稍微有点乱~ 目录 目录 -1 JVM - 19 2.1. 线程 - 20 2.2. JVM ...

最新文章

  1. 有没有想过,自己手写一个连接池?
  2. 更换XP SN的vbs
  3. mysql 开启远程访问
  4. mysql decode encode 乱码问题
  5. 斯坦福CS231n 2017最新课程:李飞飞详解深度学习的框架实现与对比
  6. 构造器Constructor是否可被override
  7. base64编解码的类
  8. Linux---cacti安装后不出图
  9. SD Card Formatter for Mac Download
  10. LInux 下安装 python notebook 及指向路径,运行计时,炫酷的深蓝午夜主题,本地登陆远程服务器
  11. java socket通讯_Java socket通讯实现过程及问题解决
  12. flex布局的一点注意点
  13. mysql优化方法_mysql优化方案总结
  14. Python教程大纲
  15. Bamboo 0.2.11 发布,HAProxy 自动配置
  16. tomcat source code in eclipse
  17. addEventListener和attachEvent
  18. Excel技巧 - 长数字串如何筛选重复项
  19. 携程酒店评论EDA及词云展示—数据来自和鲸社区
  20. PB中的timer事件

热门文章

  1. python获取eth0_python 获取网卡实时流量
  2. hashmap扩容_我说我了解集合类,面试官竟然问我为啥HashMap的负载因子不设置成1!?
  3. js里面把密码encode_Python实战案例:这是你见过的最详细的JS加密登录某博
  4. Javascript:js借助jQuery和fileSave将表格存储到world
  5. c语言数据结构单链表输出链表操作,单链表一系列操作c语言实现(按照严蔚敏那本数据结构编写)...
  6. 使用缓冲字符流BufferedReader和文件字符流FileReader读取文本文件
  7. ArcGIS 10.5 及 ArcGIS Server下载安装破解环境配置
  8. 图像形态学运算之腐蚀-膨胀篇
  9. SDOD: Real-time Segmenting and Detecting 3D Objects by Depth(实时3D检测与分割)
  10. utf8_general_ci、utf8_unicode_ci和utf8_bin的区别