AtomicLong的原理

  AtomicLong是通过依靠底层的CAS来保障原子性的更新数据,在要添加或者减少的时候,会使用死循环不断地cas到特定的值,从而达到更新数据的目的。

LongAdder的原理

  LongAdder是在AtomicLong的基础上将单点更新压力分散到各个节点,在低并发的时候通过对八色的直接更新可以很好的保障和AtomicLong的性能基本保持一致,而在高并发的时候通过分散提高了性能。缺点就是LongAdder在统计的时候如果有并发更新,可能导致统计的数据有误差。

LongAdder继承了Striped64类

public class LongAdder extends Striped64 implements Serializable
 
LongAdder继承了Striped64类,来实现累加功能的,它是实现高并发累加的工具类; 
Striped64的设计核心思路就是通过内部的分散计算来避免竞争。 
Striped64内部包含一个base和一个Cell[] cells数组,又叫hash表。 
没有竞争的情况下,要累加的数通过cas累加到base上;如果有竞争的话,会将要累加的数累加到Cells数组中的某个cell元素里面。所以整个Striped64的值为sum=base+∑[0~n]cells。
Striped64内部三个重要的成员变量:
/**
* 存放Cell的hash表,大小为2的幂。
*/
transient volatile Cell[] cells;
/**
* 基础值,
* 1. 在没有竞争时会更新这个值;
* 2. 在cells初始化的过程中,cells处于不可用的状态,这时候也会尝试将通过cas操作值累加到base。
*/
transient volatile long base;
/**
* 自旋锁,通过CAS操作加锁,用于保护创建或者扩展Cell表。
*/
transient volatile int cellsBusy;
 

成员变量cells

cells数组是LongAdder高性能实现的必杀器: 
AtomicInteger只有一个value,所有线程累加都要通过cas竞争value这一个变量,高并发下线程争用非常严重; 
而LongAdder则有两个值用于累加,一个是base,它的作用类似于AtomicInteger里面的value,在没有竞争的情况不会用到cells数组,它为null,这时使用base做累加,有了竞争后cells数组就上场了,第一次初始化长度为2,以后每次扩容都是变为原来的两倍,直到cells数组的长度大于等于当前服务器cpu的数量为止就不在扩容(想下为什么到超过cpu数量的时候就不再扩容);每个线程会通过线程对cells[threadLocalRandomProbe%cells.length]位置的Cell对象中的value做累加,这样相当于将线程绑定到了cells中的某个cell对象上;
 

成员变量cellsBusy

cellsBusy,它有两个值0 或1,它的作用是当要修改cells数组时加锁,防止多线程同时修改cells数组,0为无锁,1为加锁,加锁的状况有三种 
1. cells数组初始化的时候; 
2. cells数组扩容的时候; 
3. 如果cells数组中某个元素为null,给这个位置创建新的Cell对象的时候;

成员变量base

它有两个作用: 
1. 在开始没有竞争的情况下,将累加值累加到base 
2. 在cells初始化的过程中,cells不可用,这时会尝试将值累加到base上;

Cell内部类

//为提高性能,使用注解@sun.misc.Contended,用来避免伪共享,
@sun.misc.Contended static final class Cell {
//用来保存要累加的值
volatile long value;
Cell(long x) { value = x; }
//使用UNSAFE类的cas来更新value值
final boolean cas(long cmp, long val) {
returnUNSAFE.compareAndSwapLong(this, valueOffset, cmp, val);
}
private static final sun.misc.Unsafe UNSAFE;
//value在Cell类中存储位置的偏移量;
private static final long valueOffset;
//这个静态方法用于获取偏移量
static {
try {
UNSAFE = sun.misc.Unsafe.getUnsafe();
Class<?> ak = Cell.class;
valueOffset = UNSAFE.objectFieldOffset
(ak.getDeclaredField("value"));
} catch (Exception e) {
throw new Error(e);
}
}
}
 
这个类很简单,final类型,内部有一个value值,使用cas来更新它的值;Cell类唯一需要注意的地方就是Cell类的注解@sun.misc.Contended。
 
 
 

AtomicLong与LongAdder的区别的更多相关文章

  1. AtomicLong和LongAdder的区别

    AtomicLong的原理是依靠底层的cas来保障原子性的更新数据,在要添加或者减少的时候,会使用死循环不断地cas到特定的值,从而达到更新数据的目的. LongAdder在AtomicLong的基础 ...

  2. java多线程之AtomicLong与LongAdder

    AtomicLong简要介绍 AtomicLong是作用是对长整形进行原子操作,显而易见,在java1.8中新加入了一个新的原子类LongAdder,该类也可以保证Long类型操作的原子性,相对于At ...

  3. 【Java多线程】AtomicLong和LongAdder

    AtomicLong简要介绍 AtomicLong是作用是对长整形进行原子操作,显而易见,在java1.8中新加入了一个新的原子类LongAdder,该类也可以保证Long类型操作的原子性,相对于At ...

  4. [JDK8]性能优化之使用LongAdder替换AtomicLong

    如果让你实现一个计数器,有点经验的同学可以很快的想到使用AtomicInteger或者AtomicLong进行简单的封装. 因为计数器操作涉及到内存的可见性和线程之间的竞争,而Atomic***的实现 ...

  5. LongAdder和AtomicLong性能对比

    jdk1.8中新原子操作封装类LongAdder和jdk1.5的AtomicLong和synchronized的性能对比,直接上代码: package com.itbac.cas; import ja ...

  6. LongAdder源码分析

    AtomicLong是作用是对长整形进行原子操作,显而易见,在java1.8中新加入了一个新的原子类LongAdder,该类也可以保证Long类型操作的原子性,相对于AtomicLong,LongAd ...

  7. 一篇文章快速搞懂 Atomic(原子整数/CAS/ABA/原子引用/原子数组/LongAdder)

    前言 相信大部分开发人员,或多或少都看过或写过并发编程的代码.并发关键字除了Synchronized,还有另一大分支Atomic.如果大家没听过没用过先看基础篇,如果听过用过,请滑至底部看进阶篇,深入 ...

  8. 我从LongAdder中窥探到了高并发的秘籍,上面只写了两个字...

    这是why的第 53 篇原创文章 荒腔走板 大家好,我是why. 时间过的真是快,一周又要结束了.那么,你比上周更博学了吗?先来一个简短的荒腔走板,给冰冷的技术文注入一丝色彩. 上面这图是我之前拼的一 ...

  9. Java并发编程(四):线程安全性

    一.定义 当多个线程访问某个类时,不管运行时环境采用何种调度方式或者这些进程将如何交替执行,并且在主调代码中不需要额外的同步或协同,这个类都能表现出正确的行为,那么就称这个类是线程安全的. 二.线程安 ...

随机推荐

  1. 下载YouTube视频的方法

    这个网站就可以: http://www.clipconverter.cc/ 更多的网站及介绍参考知乎:http://www.zhihu.com/question/19964181

  2. Junit4用法

    序号 方法和描述 1 void assertEquals(boolean expected, boolean actual) 检查两个变量或者等式是否平衡 2 void assertTrue(bool ...

  3. centos7.3安装redis

    yum install epel-release yum install redis 如果支持从其他机器能访问,需要修改配置文件 /etc/redis.conf,注释掉 bin 127.0.0.1 如 ...

  4. python3.4学习笔记(二十) python strip()函数 去空格\n\r\t函数的用法

    python3.4学习笔记(二十) python strip()函数 去空格\n\r\t函数的用法 在Python中字符串处理函数里有三个去空格(包括'\n', '\r', '\t', ' ')的函数 ...

  5. 20145322 Exp5 利用nmap扫描

    20145322 Exp5 利用nmap扫描 实验过程 使用命令创建一个msf所需的数据库 service postgresql start msfdb start 使用命令msfconsole开启m ...

  6. 解决Navicat Premium 12 连接oracle数据库出现ORA-28547的问题

    1. 出现的问题... 下午工作时想连接Oracle数据库,使用的是Navicat Premium 12 . 数据库地址.用户名.密码.端口号都没有问题,但出现了ORA-28547:connectio ...

  7. __NSCFConstantString && __NSPlaceholderDictionary

    一 -[__NSCFConstantString size]: unrecognized selector sent to instance 0x53ea70 该错误是在我将NSString类型的参数 ...

  8. 理解 python 中__name__ = '__main__' 的作用

    很多新手刚开始学习python的时候经常会看到python 中__name__ = \'__main__\' 这样的代码,可能很多新手一开始学习的时候都比较疑惑,python 中__name__ = ...

  9. 如何在windows中部署Gitblit

    1.安装Java环境 2.下载Gitblit压缩包 http://gitblit.com/ 3.解压后进行配置 编辑gitblit-1.8.0\data\gitblit.properties文件 gi ...

  10. Linux下指定pip install和make install安装路径

    在Linux下直接用pip install packageName,有些文件会被放到根目录下,如果没有sudo权限的话,是会安装失败的.这个以后我们就需要指定安装的目录了. pip install - ...