精彩理解:  https://www.jianshu.com/p/21be831e851e ;  https://blog.csdn.net/heyutao007/article/details/19975665 ;

备选参考:https://blog.csdn.net/tanga842428/article/details/52742698; https://www.cnblogs.com/yitong0768/p/4555445.html ;

CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。

这样处理的逻辑是,首先检查某块内存的值是否跟之前我读取时的一样,如不一样则表示期间此内存值已经被别的线程更改过,舍弃本次操作,否则说明期间没有其他线程对此内存值操作,可以把新值设置给此块内存。CAS的原子性是由 CPU的CPI硬件指令实现保证的,即调用native方法调用由C++编写的硬件级别指令,jdk中提供Unsafe类执行这些操作。

一.直入正题

现在有这么一个需求,需要实现一个支持并发的计数功能,例如下面的代码

public class Increment{
  private int count=0;
  //多线程环境下对count执行++操作
}

面试官可能会提供上述代码问你,你觉得这个操作有什么问题,如果你说没有问题,那么恭喜你,大兄嘚,你可以走了,面试到此结束!

如果你说这段代码有问题,因为在并发环境下对count进行自增运算是不安全,好了,兄嘚,你入坑了。接着面试官会问问什么不安全以及如何解决这个问题呢?

二.为什么并发环境下的count自增操作不安全

因为count++不是原子操作,而是三个原子操作的组合:

  1. 读取内存中的count值赋值给局部变量temp
  2. 执行temp+1操作
  3. 将temp赋值给count

所以如果两个线程同时执行count++操作的话,我们不能保证线程1按顺序执行完上述三步后线程2才开始执行。

面试官露出了满(yin)意(xian)的笑容,兄嘚你知道的太多了,好吧,咱们继续,既然你说这么操作是不安全的,那么怎么解决呢?

三.并发环境下count++不安全问题的解决方案

  1. synchronized加锁
public class Increment{
  private int count=0;
  public synchronized void add(){
    count++
  }
}

加锁后代码变成上面这样了,好了现在就是线程安全的了,同一时间只有一个线程能加锁,其他线程需要等待锁,这样就不会出现count计数不准确的问题了。

面试官还是不想放过你,接着问,这个方案还有没有可能优化一下呢?

引入synchronized会造成多个线程排队的问题,所以同一时间只有一个线程执行,这样的锁有点儿“重量级”了

虽然随着Java版本更新,也对synchronized做了很多优化,但是处理这种简单的累加操作,仍然显得“太重了”。人家synchronized是可以解决更加复杂的并发编程场景和问题的。

而且,在这个场景下,你要是用synchronized,不就相当于让各个线程串行化了么?一个接一个的排队,加锁,处理数据,释放锁,下一个再进来。

2.高效的方案Atomic原子类

你思索片刻说:对于这种的count++类的操作,我们完全可以换一种做法,java并发包下面提供了一系列的Atomic原子类,比如说AtomicInteger

public class Increment{
  private AtomicInteger count=new AtomicInteger();
  public synchronized void add(){
    count.incrementAndGet();
  }
}

多个线程可以并发的执行AtomicInteger的incrementAndGet()方法,意思就是给我把count的值累加1,接着返回累加后最新的值,实际上,Atomic原子类底层用的不是传统意义的锁机制,而是无锁化的CAS机制,通过CAS机制保证多线程修改一个数值的安全性。

你心想:看到没我都知道,这种事情难不倒我

这时面试官喝了口水说:小伙子懂得真多

你想这次面试应该问完了吧,可是没想到面试官接着又抛出一个问题:CAS底层实现原理是什么?

靠,没完了是吧,还问底层原理,也怪自己多嘴,非得说什么CAS,没办法,自己挖的坑说什么也得填上啊

流程图如下:

假如说有3个线程并发的要修改一个AtomicInteger的值,他们底层的机制如下:

首先,每个线程都会先获取当前的值,接着走一个原子的CAS操作,原子的意思就是这个CAS操作一定是自己完整执行完的,不会被别人打断。

然后CAS操作里,会比较一下,现在你的值是不是刚才我获取到的那个值啊?

如果是的话,OK!说明没人改过这个值,那你给我设置成累加1之后的一个值!

同理,如果有人在执行CAS的时候,发现自己之前获取的值跟当前的值不一样,会导致CAS失败,失败之后,进入一个无限循环,再次获取值,接着执行CAS操作!

说完连自己都佩服自己了

心想可以了吧,结果面试官又抛出一个问题:CAS有没有什么问题?

3.CAS性能优化

真是要了亲命了,还有完没完啊,我想静静了!!!!

这个CAS有什么问题呢?从上面的流程图其实可以看出来,比如说大量的线程同时并发修改一个AtomicInteger,可能有很多线程会不停的自旋,进入一个无限重复的循环中。

这些线程不停地获取值,然后发起CAS操作,但是发现这个值被别人改过了,于是再次进入下一个循环,获取值,发起CAS操作又失败了,再次进入下一个循环。

在大量线程高并发更新AtomicInteger的时候,这种问题可能会比较明显,导致大量线程空循环,自旋转,性能和效率都不是特别好,这个问题说到这里,恭喜你,你已经击败了80%的对手了大兄嘚!!!那么如何优化呢?

Java 8有一个新的类,LongAdder,他就是尝试使用分段CAS以及自动分段迁移的方式来大幅度提升多线程高并发执行CAS操作的性能,这个类具体是如何优化性能的呢?咱们看图说话:

LongAdder核心思想就是热点分离,这一点和ConcurrentHashMap的设计思想。就是将value值分离成一个数组,当多线程访问时,通过hash算法映射到其中的一个数字进行计数。而最终的结果,就是这些数组的求和累加。这样一来,就减小了锁的粒度

LongAddr的兄弟类如下:

说到这里面试官终于说话:行,这个问题就问道这里了!!!

 
转自:https://www.toutiao.com/a6660643231585272331/?tt_from=c&iid=59710992155&app=news_article×tamp%3D1550844890&group_id=6660643231585272331

Java CAS同步机制 原理详解(为什么并发环境下的COUNT自增操作不安全): Atomic原子类底层用的不是传统意义的锁机制,而是无锁化的CAS机制,通过CAS机制保证多线程修改一个数值的安全性。的更多相关文章

  1. [转]DNS服务器原理详解与Centos6.x下搭建DNS服务器

    转自:http://blog.it985.com/8958.html DNS 数据库的记录:正解,反解, Zone 的意义 通过DNS解析过程详解这篇文章,我们知道了要想访问www.zmit.cn,最 ...

  2. java线程同步: synchronized详解(转)

    Java语言的关键字,当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码. 一.当两个并发线程访问同一个对象object中的这个synchronized(this ...

  3. Java GC的工作原理详解

    JVM学习笔记之JVM内存管理和JVM垃圾回收的概念,JVM内存结构由堆.栈.本地方法栈.方法区等部分组成,另外JVM分别对新生代下载地址  和旧生代采用不同的垃圾回收机制. 首先来看一下JVM内存结 ...

  4. 【Java基础】HashMap原理详解

    哈希表(hash table) 也叫散列表,是一种非常重要的数据结构,应用场景及其丰富,许多缓存技术(比如memcached)的核心其实就是在内存中维护一张大的哈希表,本文会对java集合框架中Has ...

  5. Android4.3 屏蔽HOME按键返回桌面详解(源码环境下)

    点击打开链接 首先声明我是做系统开发的(高通平台),所以下面介绍的方法并不适合应用开发者. 最经有个需求要屏蔽HOME按键返回桌面并且实现自己的功能,发现以前的方式报错用不了,上网搜索了一下,发现都是 ...

  6. CAS的ABA问题详解

    CAS的ABA问题详解 ABA问题 在多线程场景下CAS会出现ABA问题,关于ABA问题这里简单科普下,例如有2个线程同时对同一个值(初始值为A)进行CAS操作,这三个线程如下 1.线程1,期望值为A ...

  7. 锁之“轻量级锁”原理详解(Lightweight Locking)

    大家知道,Java的多线程安全是基于Lock机制实现的,而Lock的性能往往不如人意. 原因是,monitorenter与monitorexit这两个控制多线程同步的bytecode原语,是JVM依赖 ...

  8. 【转载】JAVA消息服务JMS规范及原理详解

    转载:https://www.cnblogs.com/molao-doing/articles/6557305.html 作者: moyun- 一.简介 JMS即Java消息服务(Java Messa ...

  9. JAVA消息服务JMS规范及原理详解

    JAVA消息服务JMS规范及原理详解 一.简介 JMS即Java消息服务(Java Message Service)应用程序接口,是一个Java平台中关于面向消息中间件(MOM)的API,用于在两个应 ...

随机推荐

  1. vim常用指令

    命令历史 以:和/开头的命令都有历史纪录,可以首先键入:或/然后按上下箭头来选择某个历史命令. 启动vim 在命令行窗口中输入以下命令即可 vim 直接启动vim vim filename 打开vim ...

  2. java基础3(二)

    基础加强 类加载器 A.类加载器获取classpath下任意内容 注意: 可以通过ClassLoader可以获取classpath下的所有内容. SpringMVC 1.整体架构 流程图 2.流程细节 ...

  3. 浅谈Vector、ArrayList、LinkedList

    下图是Collection的类继承图 从图中可以看出:Vector.ArrayList.LinkedList这三者都实现了List 接口.所有使用方式也很相似,主要区别在于实现方式的不同,所以对不同的 ...

  4. hadoop2.6.0高可靠及yarn 高可靠搭建

    以前用hadoop2.2.0只搭建了hadoop的高可用,但在hadoop2.2.0中始终没有完成YARN HA的搭建,直接下载了hadoop最新稳定版本2.6.0完成了YARN HA及HADOOP ...

  5. java基础知识—继承

    1.不能被继承的父类成员: private成员.子类与父类不在同包,使用默认访问权限的成员.构造方法. 2.访问修饰符: 访问修饰符        本类       同包      子类     其它 ...

  6. Shell bash 数学运算 bc

    1.bc命令可以完成浮点数的运算.其中 scale可以指定保留的小数点位数. 2.举例 例1: 例2:

  7. 三星Galaxy S8 刷机经验记录

    这段时间用上了三星S8,由于原生系统太耗电,所以萌生了root的想法.写这篇博客记录下这段时间的各种尝试. Root过程说明: 友情提示,道路千万条,安全第一条.开始捣鼓手机之前请一定准备好官方的救砖 ...

  8. Vim+Taglist+AutoComplPop之代码目录分栏信息和自动补全提示(Ubuntu环境)

    一步: 首先在Ubuntu环境中安装ctags:  sudo apt-get install ctags 第二部:       安装Taglist-------------Taglist是vim的一个 ...

  9. Fiddler抓不到https的解决办法

    这个问题可能是证书的问题,操作步骤如下:  Step 1:打开系统证书管理器certmgr.msc,点击操作–查找证书,然后输入fiddler查找所有相关证书,将查找出来的结果全部删除: Step 2 ...

  10. hive防止数据被误删除

    1.HDFS层面开启trash功能(fs.trash.interval) 被删除的数据在HDFS中的/user/$USER/.Trash目录中,开启这个功能秩序要将配置属性fs.trash.inter ...