精彩理解:  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. sql 约束Check中使用Case函数

    CHECK 约束用于限制列中的值的范围 在Check中使用Case函数在很多情况下都是非常不错的解决方法.可能有很多人根本就不用Check,那么我建议你在看过下面的例子之后也尝试一下在SQL中使用Ch ...

  2. vue 特点

    1.双向绑定  v-model 2.组件化  页面扩展 单文件组件 js css html 都在一个文件中 好处:前端组件化的突破性设计 scoped限制css的渲染,防止污染 lang 定义预处理器 ...

  3. SQL-53 按照dept_no进行汇总,属于同一个部门的emp_no按照逗号进行连接,结果给出dept_no以及连接出的结果employees

    题目描述 按照dept_no进行汇总,属于同一个部门的emp_no按照逗号进行连接,结果给出dept_no以及连接出的结果employeesCREATE TABLE `dept_emp` (`emp_ ...

  4. 拷贝一张图片,从一个目录到另外一个目录下(PS:是拷贝是不是移动)

    package com.lanxi.demo2_6; import java.io.File; import java.io.FileInputStream; import java.io.FileN ...

  5. .NET--------未能加载文件或程序集“System.Net.Http.Formatting”或它的某一个依赖项。

    未能加载文件或程序集“System.Net.Http.Formatting”或它的某一个依赖项.找到的程序集清单定义与程序集引用不匹配. (异常来自 HRESULT:0x80131040)   解决方 ...

  6. sse矩阵乘法 应该是1毫秒纯运算1000次

    #include <intrin.h> #include <math.h> struct Vector4 { float x, y, z, w; }; struct Matri ...

  7. seckill(1)秒杀系统主要步骤

  8. python+appium 自动化2--元素定位uiautomatorviewer

    出处:https://www.cnblogs.com/yoyoketang/p/6128741.html 前言: 可以打开手机上的app了,下一步元素定位uiautomatorviewer,通过定位到 ...

  9. c#线程池ThreadPool实例详解

    1. 如何查看线程池的最大线程数和最小线程数 static void Main(string[] args) { Console.WriteLine("----------线程池开始,线程I ...

  10. sqlserver智能提示插件-sql prompt(9.4.6)的安装及注册流程

    官网下在地址:https://www.red-gate.com/products/sql-development/sql-prompt/ CSDN下载地址(对应的版本是9.4.6,其中包含安装包和注册 ...