-
Java高并发15-AtomicLong等原子类源码解析
一、复习
-
Random类以及ThreadLocalRandom类源码解析
二、Java并发包中一些原子操作类的源码解析
1.常见类
-
例如AtomicLong,AtomicInteger,AtomicCharacter.....等这些操作类其内部实现都是通过CAS非阻塞算法来实现, 因此我们只要弄懂一个其他就基本知晓了,因此本文讲解AtomicLong类的实现原理 -
AtomicLong还有LongAdder,LongAccumulator等类是对AtomicLong特性的增强,我们后面再说
2.AtomicLong类
-
优点:使用CAS非阻塞算法实现并发,比使用synchronized等锁机制的非阻塞算法,并发性要好很多。 -
下面来看一下它的源码,注意看注释基本都解释清楚了
package com.ruigege.AtomicOperationClass4;
import java.util.concurrent.atomic.AtomicLong;
import jdk.internal.misc.Unsafe;
import java.lang.Number;
public class AtomicLongTest extends Number implements java.io.Serializable{
private static final long serialVersionUID = 1927816293512124184L;
private static final Unsafe unsafe = Unsafe.getUnsafe();
private volatile long value;//用于放实际值的变量
private final long valueOffset;//value的偏移量
static {
try {
valueOffset = unsafe.objectFieldOffset(AtomicLong.class.getDeclaredField(value));
}catch(Exception e) {
throw new Error(e);
}
}
public AtomicLongTest(long initialValue) {
value = initialValue;
}
//下面的语句是用于判断JVM是否支持Long类型的CAS
static final boolean VM_SUPPORTS_LONG_CAS = VMSupportsCS8();
private static native boolean VMSupportCS8();
/**
* 该方法使用CAS算法用于累加
* @return
*/
public final long incrementAndGet() {
return unsafe.getAndAddLong(this, valueOffset, 1L) + 1L;
}
/**
* 我们来一起回顾一下Unsafe类中的成员方法getAndAddLong方法
* public final long getAndAddLong(Object obj,long offset,long addValue){
* long l;
* do{
* l = getLongvolatie(obj,offset);//获取obj对象偏移量为offset的值
* }(!compareAndSwapLong(obj,offset,l,l+addValue));如与预期不相同,那么我们不断地获取l的值,如果
* //相同了,说明这个时候可以+addValue将得到的值赋值给l就可以了
* return l;最后返回的是+addValue之前的值
* //我理解的CAS算法,其实它的本质就是不断循环获取一致的值,再进行更新。
* }
*/
/**
* 该方法使用CAS用于累减
* @return
*/
public final long decrementAndGet() {
return unsafe.getAndAddLong(this,valueOffset,-1L) -1L;
}
/**
* 下面这两个方法,可以和上面的两个方法作对比,都是累加,但是上面返回是累加之后的值,下面是返回累加之前的值
* @return
*/
public final long getAndIncrement() {
return unsafe.getAndAddLong(this, valueOffset, 1L);
}
public final long getAndDecrement() {
return unsafe.getAndAddLong(this,valueOffset,-1L);
}
/**
* 下面这个方法类似于Unsafe中的compareAndSwapLong方法
* @param expect
* @param update
* @return
*/
public final boolean compareAndSet(long expect,long update) {
return unsafe.compareAndSwapLong(this,valueOffset,expect,update);
}
}
-
上面的代码是JDK8中的代码,我们可以拿出一个方法getAndIncrement,来对比再JDK7中的实现源码
public final int getAndIncrement(){
while(true){
long temp = get();//这一行就是或者value的值
long newTemp = temp + 1;
if(compareAndSet(temp,newTemp){
//这里面的判断是使用了该类里面测成员方法
return temp;
}
}
}
-
这个循环的意义其实就是一个CAS操作,我们不断循环获取value的值, 然后一致了,就开始设置,不一致的时候,就不断的开始获取,直到一致。JDK8其实不过是使用了Unsafe类作为工具类,来实现CAS操作。 -
在没有原子类的时候,我们使用锁synchronize机制来保证一致性,会大大降低并发效率;使用原子类,提高了效率,但是在大规模的并发下,这个类还是存在效率低的问题,因此引出今日主角
3.LongAdder类
-
atomicLong的困境以及如果优化:AtomicLong类的问题在于,在高并发的情况下,所有的线程都会区竞争同一个共享变量,并且根据CAS机制,最后只有一个线程能够更新成功,这就导致了,其他线程为了获取共享变量,会进入到无限循环的自旋中;引入LongAdder类就是把一个共享变量分成为了多个共享变量,那么线程去竞争的时候,压力会小一些,每个线程竞争到共享变量的可能性大大增加。
-
基本构成:LongAdder内部是由一个Cell数组(需要初始化时才会创建数组,没有初始化时为null)和一个base变量,当创建该类实例以后,共享变量会变成多个Cell对象(共享变量被Cell类封装了),然后在高并发下,线程可以竞争的共享变量(Cell实例)就变多了,并且一个线程如果没有获取到Cell实例,并不会进入到自旋状态下,而是去竞争其他Cell;LongAdder实例的值,就是个各个Cell值累加,在加上base变量的值。该类其内部数组是一个延迟初始化原子性更新数组,属于惰性加载;
-
当一开始判断Cell数组时null时,所有累加操作都是在base变量上的,保持Cell数组的大小是2的N次方,在初始化数组的时候,大小为2,里面的元素是Cell实例,这个是一个Atomic改进类,用于解决伪共享问题
-
变量加载时的提升原理:对于大多数孤立的多个资源子操作进行字节填充是浪费,但是 LongAdder实例的数组可以是内存连续,因此我们需要添加个@sun.misc.Contended注解对Cell类进行填充,这样按行加载内存机制就得到优化,提升性能。
-
详细原理我们下次再说
三、源码:
-
所在包:com.ruigege.AtomicOperationClass4 -
https://github.com/ruigege66/ConcurrentJava
-
CSDN:https://blog.csdn.net/weixin_44630050 -
博客园:https://www.cnblogs.com/ruigege0000/
出 处:https://www.cnblogs.com/ruigege0000/p/14161566.html
最新更新
python爬虫及其可视化
使用python爬取豆瓣电影短评评论内容
nodejs爬虫
Python正则表达式完全指南
爬取豆瓣Top250图书数据
shp 地图文件批量添加字段
爬虫小试牛刀(爬取学校通知公告)
【python基础】函数-初识函数
【python基础】函数-返回值
HTTP请求:requests模块基础使用必知必会
SQL SERVER中递归
2个场景实例讲解GaussDB(DWS)基表统计信息估
常用的 SQL Server 关键字及其含义
动手分析SQL Server中的事务中使用的锁
openGauss内核分析:SQL by pass & 经典执行
一招教你如何高效批量导入与更新数据
天天写SQL,这些神奇的特性你知道吗?
openGauss内核分析:执行计划生成
[IM002]Navicat ODBC驱动器管理器 未发现数据
初入Sql Server 之 存储过程的简单使用
uniapp/H5 获取手机桌面壁纸 (静态壁纸)
[前端] DNS解析与优化
为什么在js中需要添加addEventListener()?
JS模块化系统
js通过Object.defineProperty() 定义和控制对象
这是目前我见过最好的跨域解决方案!
减少回流与重绘
减少回流与重绘
如何使用KrpanoToolJS在浏览器切图
performance.now() 与 Date.now() 对比