Jdk1.8 JUC源码增量解析(2)-atomic-LongAdder和LongAccumulator
作者:大飞
功能简介:
- LongAdder是jdk1.8提供的累加器,基于Striped64实现。它常用于状态采集、统计等场景。AtomicLong也可以用于这种场景,但在线程竞争激烈的情况下,LongAdder要比AtomicLong拥有更高的吞吐量,但会耗费更多的内存空间。
- LongAccumulator和LongAdder类似,也基于Striped64实现。但要比LongAdder更加灵活(要传入一个函数接口),LongAdder相当于是LongAccumulator的一种特例。
源码分析:
- 先看一下LongAdder类,看下结构:
public class LongAdder extends Striped64 implements Serializable { private static final long serialVersionUID = 7249069246863182397L; /** * Creates a new adder with initial sum of zero. */ public LongAdder() { }
LongAdder继承了Striped64,本身没有任何域。
- 再看一下LongAdder的方法:
public void add(long x) { Cell[] as; long b, v; int m; Cell a; //如果cell表为null,会尝试将x累加到base上。 if ((as = cells) != null || !casBase(b = base, b + x)) { /* * 如果cell表不为null或者尝试将x累加到base上失败,执行以下操作。 * 如果cell表不为null且通过当前线程的probe值定位到的cell表中的Cell不为null。 * 那么尝试累加x到对应的Cell上。 */ boolean uncontended = true; if (as == null || (m = as.length - 1) < 0 || (a = as[getProbe() & m]) == null || !(uncontended = a.cas(v = a.value, v + x))) //或者cell表为null,或者定位到的cell为null,或者尝试失败,都会调用下面的Striped64中定义的longAccumulate方法。 longAccumulate(x, null, uncontended); } }
add方法逻辑很简单,先尝试将x累加到base上,失败的话再看看能不能从cell表中找到cell,找到的话再尝试将x累加到这个cell里面,还失败的话就调用longAccumulate方法,这个方法上篇分析Striped64的时候分析过。
/** * Equivalent to {@code add(1)}. */ public void increment() { add(1L); } /** * Equivalent to {@code add(-1)}. */ public void decrement() { add(-1L); }
递增和递减方法,不需要解释了。
public long sum() { Cell[] as = cells; Cell a; long sum = base; if (as != null) { for (int i = 0; i < as.length; ++i) { if ((a = as[i]) != null) sum += a.value; } } return sum; }
sum方法就是获取当前LongAdder值的总和,包括base和cells value两部分。
public void reset() { Cell[] as = cells; Cell a; base = 0L; if (as != null) { for (int i = 0; i < as.length; ++i) { if ((a = as[i]) != null) a.value = 0L; } } }
重置方法,将base和cells value两部分值都置为0。
public long sumThenReset() { Cell[] as = cells; Cell a; long sum = base; base = 0L; if (as != null) { for (int i = 0; i < as.length; ++i) { if ((a = as[i]) != null) { sum += a.value; a.value = 0L; } } } return sum; }
获取总和后重置。
LongAdder间接继承了Number,看下相关的方法实现:
public long longValue() { return sum(); } public int intValue() { return (int)sum(); } public float floatValue() { return (float)sum(); } public double doubleValue() { return (double)sum(); }
LongAdder的序列化使用序列化代理模式:
private static class SerializationProxy implements Serializable { private static final long serialVersionUID = 7249069246863182397L; private final long value; SerializationProxy(LongAdder a) { value = a.sum(); } private Object readResolve() { LongAdder a = new LongAdder(); a.base = value; return a; } } private Object writeReplace() { return new SerializationProxy(this); } private void readObject(java.io.ObjectInputStream s) throws java.io.InvalidObjectException { throw new java.io.InvalidObjectException("Proxy required"); }
- 再看一下LongAccumulator类,先看结构:
public class LongAccumulator extends Striped64 implements Serializable { private static final long serialVersionUID = 7249069246863182397L; private final LongBinaryOperator function; private final long identity; public LongAccumulator(LongBinaryOperator accumulatorFunction, long identity) { this.function = accumulatorFunction; base = this.identity = identity; }
LongAccumulator和LongAdder不同,内部有一个函数接口和一个初始值。
- 再看LongAccumulator的方法:
public void accumulate(long x) { Cell[] as; long b, v, r; int m; Cell a; if ((as = cells) != null || (r = function.applyAsLong(b = base, x)) != b && !casBase(b, r)) { boolean uncontended = true; if (as == null || (m = as.length - 1) < 0 || (a = as[getProbe() & m]) == null || !(uncontended = (r = function.applyAsLong(v = a.value, x)) == v || a.cas(v, r))) longAccumulate(x, function, uncontended); } }
和LongAdder的add方法一样的逻辑。
public long get() { Cell[] as = cells; Cell a; long result = base; if (as != null) { for (int i = 0; i < as.length; ++i) { if ((a = as[i]) != null) result = function.applyAsLong(result, a.value); } } return result; }
将内部所有的零散值通过函数算出一个最终值。
public void reset() { Cell[] as = cells; Cell a; base = identity; if (as != null) { for (int i = 0; i < as.length; ++i) { if ((a = as[i]) != null) a.value = identity; } } } public long getThenReset() { Cell[] as = cells; Cell a; long result = base; base = identity; if (as != null) { for (int i = 0; i < as.length; ++i) { if ((a = as[i]) != null) { long v = a.value; a.value = identity; result = function.applyAsLong(result, v); } } } return result; }
注意这里和LongAdder不同,这里的重置会将base和cells value都重置成初始值-identity。
其他的Number方法和序列化方式和LongAdder一样。
代码解析完毕!
参见:Jdk1.8 JUC源码增量解析(1)-atomic-Striped64
参见:Jdk1.6 JUC源码解析(1)-atomic-AtomicXXX
相关推荐
java-jdk1.8-8u361-all-jdk-win-linux 该压缩包中包含jdk1.8-8u361下windows版本和linux版本,其包含快速安装包和对应的jdk压缩包版本,具体内容如下: jdk-8u361-linux-aarch64.rpm jdk-8u361-linux-i586.rpm jdk-8...
2. **解压**:使用`unzip`命令解压下载的zip文件,得到`jdk-8u202-linux-x64.tar.gz`。然后使用`tar -zxvf jdk-8u202-linux-x64.tar.gz`将其解压到目标目录。 3. **配置环境变量**:为了能在系统中全局使用JDK,...
这里的"jdk-8u391-linux-aarch64.tar"是一个针对64位(aarch64架构)Linux系统的归档文件,我们需要对其进行解压并安装。 安装步骤如下: 1. **下载**:首先,你需要将JDK1.8的安装包下载到你的Linux系统中。这...
1. **下载**:从官方Oracle网站或其他可信赖的源下载`jdk-8u202-windows-x64.exe`安装程序。 2. **验证文件**:确保下载的文件完整且未被篡改,可以检查MD5或SHA校验和。 3. **双击安装**:在Windows x64系统上,...
这个压缩包"java-jdk1.8-jdk-8u181-windows-x64.zip"内包含两个文件:一个是主安装程序“jdk-8u181-windows-x64.exe”,用于在Windows 64位系统上安装JDK 1.8的更新181版本;另一个是“使用说明.txt”,通常会提供...
jdk1.8版本可用,本地测试成功,本地版本 java version "1.8.0_91" Java(TM) SE Runtime Environment (build 1.8.0_91-b14) Java HotSpot(TM) 64-Bit Server VM (build 25.91-b14, mixed mode)
总之,`jdk-8u381-macosx-aarch64.dmg`是专门为MacOS M2系统设计的JDK 1.8版本,提供了完整的开发环境,便于开发者在新的硬件平台上构建和运行Java应用。了解其安装、配置和主要特性对于提升开发效率至关重要。
安装JDK 1.8的过程非常简单,只需要双击下载的“jdk-8u201-windows-x64.exe”文件,然后按照安装向导的提示进行操作。通常,安装过程中会提供选择安装路径、是否设置环境变量等选项。为了确保Java环境的正确配置,...
这个压缩包文件“java-jdk1.8-jdk-8u191-linux-x64.zip”包含了用于在64位Linux系统上安装和使用的JDK 1.8更新191的所有必要组件。JDK(Java Development Kit)是开发和运行Java应用程序的基础,它包括了Java编译器...
Java Development Kit(JDK)是Oracle公司提供的用于开发和运行Java应用程序的软件工具包。"jdk1.8-windows-32位-免安装"是指针对Windows操作系统,适用于32位系统的JDK 1.8版本,它具有无需安装的特点,用户只需将...
2. 这将解压出名为`jdk-8u201-linux-x64.tar.gz`的文件,接着使用`tar -zxvf jdk-8u80-linux-x64.tar.gz`将其解压到指定目录。 3. 配置环境变量。打开或创建`~/.bashrc`或`~/.bash_profile`文件,并添加以下行: ``...
JDK1.8安装文件安装即可,非常Nice~ 步骤1:下载JDK 首先,您需要把当前资源下载下来节约到指定目录 步骤2:安装JDK 一旦下载完成,您需要运行安装程序来安装JDK。如果您下载的是压缩包,需要解压缩到您希望安装...
jdk1.8 32位官方正式版 jdk-8u91-windows-i586
JDK 1.8 jdk-8u171-windows-x64
Linux x64版本并下载jdk-8u212-linux-x64.tar.gz文件。 将下载的文件解压缩到您选择的目录中。 配置环境变量,使系统能够找到Java运行时环境。可以通过设置JAVA_HOME和PATH环境变量来实现。
2. Eclipse JEE版本:Eclipse提供了多种版本,其中JEE版本是专为Java企业级开发设计的,包含了一系列用于Web和企业应用开发的工具,如支持Java EE框架(如Spring、Struts等)、服务器集成(如Tomcat、GlassFish等)...
这个压缩包“java-jdk1.8-jdk-8u192-linux-x64.zip”包含了用于在64位Linux系统上运行和开发Java应用程序所需的组件。JDK全称为Java Development Kit,它是Java编程语言的核心组成部分,提供了编译、调试、运行Java...
标题中的"jdk-1.8-8u77-windows-x64.7z"指的是JDK 1.8的第77次更新,专为64位Windows系统设计的压缩文件。这个版本的JDK包含了Java运行时环境(JRE)和开发工具,是开发者进行Java编程的必备组件。7z格式是一种高效...
jdk1.8 jdk-8u5-windows-i586 32位官方正式版 jdk1.8 jdk-8u5-windows-i586 32位官方正式版
关于安装步骤,描述中提到“双击安装即可”,这意味着在Windows x64环境下,用户只需要下载`jdk-8u191-windows-x64.exe`这个安装文件,然后双击运行,按照安装向导的提示进行操作,选择安装路径,确认许可协议,最后...