Java CAS同步机制 原理详解(为什么并发环境下的COUNT自增操作不安全): Atomic原子类底层用的不是传统意义的锁机制,而是无锁化的CAS机制,通过CAS机制保证多线程修改一个数值的安全性。
精彩理解: 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++不是原子操作,而是三个原子操作的组合:
- 读取内存中的count值赋值给局部变量temp
- 执行temp+1操作
- 将temp赋值给count
所以如果两个线程同时执行count++操作的话,我们不能保证线程1按顺序执行完上述三步后线程2才开始执行。
面试官露出了满(yin)意(xian)的笑容,兄嘚你知道的太多了,好吧,咱们继续,既然你说这么操作是不安全的,那么怎么解决呢?
三.并发环境下count++不安全问题的解决方案
- 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的兄弟类如下:
说到这里面试官终于说话:行,这个问题就问道这里了!!!
Java CAS同步机制 原理详解(为什么并发环境下的COUNT自增操作不安全): Atomic原子类底层用的不是传统意义的锁机制,而是无锁化的CAS机制,通过CAS机制保证多线程修改一个数值的安全性。的更多相关文章
- [转]DNS服务器原理详解与Centos6.x下搭建DNS服务器
转自:http://blog.it985.com/8958.html DNS 数据库的记录:正解,反解, Zone 的意义 通过DNS解析过程详解这篇文章,我们知道了要想访问www.zmit.cn,最 ...
- java线程同步: synchronized详解(转)
Java语言的关键字,当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码. 一.当两个并发线程访问同一个对象object中的这个synchronized(this ...
- Java GC的工作原理详解
JVM学习笔记之JVM内存管理和JVM垃圾回收的概念,JVM内存结构由堆.栈.本地方法栈.方法区等部分组成,另外JVM分别对新生代下载地址 和旧生代采用不同的垃圾回收机制. 首先来看一下JVM内存结 ...
- 【Java基础】HashMap原理详解
哈希表(hash table) 也叫散列表,是一种非常重要的数据结构,应用场景及其丰富,许多缓存技术(比如memcached)的核心其实就是在内存中维护一张大的哈希表,本文会对java集合框架中Has ...
- Android4.3 屏蔽HOME按键返回桌面详解(源码环境下)
点击打开链接 首先声明我是做系统开发的(高通平台),所以下面介绍的方法并不适合应用开发者. 最经有个需求要屏蔽HOME按键返回桌面并且实现自己的功能,发现以前的方式报错用不了,上网搜索了一下,发现都是 ...
- CAS的ABA问题详解
CAS的ABA问题详解 ABA问题 在多线程场景下CAS会出现ABA问题,关于ABA问题这里简单科普下,例如有2个线程同时对同一个值(初始值为A)进行CAS操作,这三个线程如下 1.线程1,期望值为A ...
- 锁之“轻量级锁”原理详解(Lightweight Locking)
大家知道,Java的多线程安全是基于Lock机制实现的,而Lock的性能往往不如人意. 原因是,monitorenter与monitorexit这两个控制多线程同步的bytecode原语,是JVM依赖 ...
- 【转载】JAVA消息服务JMS规范及原理详解
转载:https://www.cnblogs.com/molao-doing/articles/6557305.html 作者: moyun- 一.简介 JMS即Java消息服务(Java Messa ...
- JAVA消息服务JMS规范及原理详解
JAVA消息服务JMS规范及原理详解 一.简介 JMS即Java消息服务(Java Message Service)应用程序接口,是一个Java平台中关于面向消息中间件(MOM)的API,用于在两个应 ...
随机推荐
- @Dependson注解与@ConditionalOnBean注解的区别
@Dependson注解是在另外一个实例创建之后才创建当前实例,也就是,最终两个实例都会创建,只是顺序不一样 @ConditionalOnBean注解是只有当另外一个实例存在时,才创建,否则不创建,也 ...
- linux 免密码登陆
1.Linux下生成密钥 ssh-keygen的命令手册,通过”man ssh-keygen“命令: 通过命令”ssh-keygen -t rsa“ 生成之后会在用户的根目录生成一个 “.ssh”的文 ...
- BluePrism初尝
由于对工作的需求,现在开始接触了RPA. RPA是什么?第一次看见这个名词,我脑海里只有RPG的概念.一番查询,才知道是Robotic Process Automation的英文缩写,机器人流程自动化 ...
- YYY0.1YYY
XXX0.2XXXZZZ目前提供免费下载和自动更新功能,由于自己的时间有限,就我一个开发,后期持续更新在线听歌.mp4转mp3.高音质下载.全球dj网站免费下载功能!ZZZ
- Java中的公平锁和非公平锁实现详解
前言 Java语言中有许多原生线程安全的数据结构,比如ArrayBlockingQueue.CopyOnWriteArrayList.LinkedBlockingQueue,它们线程安全的实现方式并非 ...
- WPF 系统关闭模式
WPF App.xaml中ShutdownMode的属性值 OnLastWindowClose(默认值) 最后一个窗体关闭或调用Application对象的Shutdown()方法时,应用程序关闭. ...
- 关于redis的安装
下载redis 我这边下载redis4.0.1版本, 然后解压 然后执行yum -y install gcc gcc-c++ libstdc++-devel 然后cd //qbtapp/redis-4 ...
- 泊爷带你学go -- redis连接池的操作
package main import ( "common" "fmt" "proto" "strconv" " ...
- NHibernate4使用Oracle.ManagedDataAccess.dll连接oracle及配置多个数据库连接
NHibernate数据库配置参数在hibernate.cfg.xml中 <?xml version="1.0" encoding="utf-8"?> ...
- java后端的知识学习
有良好的计算机基础知识,熟悉计算机网络技术以及常用的设计模式:有扎实的 Java 语言基础,熟悉 Java 多线程编程技术.JVM 虚拟机原理:熟悉J2EE体系架构,熟悉WebService.Spri ...