`
BrokenDreams
  • 浏览: 254130 次
  • 性别: Icon_minigender_1
  • 来自: 北京
博客专栏
68ec41aa-0ce6-3f83-961b-5aa541d59e48
Java并发包源码解析
浏览量:100215
社区版块
存档分类
最新评论

Jdk1.8 JUC源码增量解析(2)-atomic-LongAdder和LongAccumulator

阅读更多

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

    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...

    java-jdk1.8-jdk-8u202-linux-x64.zip

    2. **解压**:使用`unzip`命令解压下载的zip文件,得到`jdk-8u202-linux-x64.tar.gz`。然后使用`tar -zxvf jdk-8u202-linux-x64.tar.gz`将其解压到目标目录。 3. **配置环境变量**:为了能在系统中全局使用JDK,...

    JDK1.8安装包Linux可用(jdk-8u391-linux-aarch64.tar)

    这里的"jdk-8u391-linux-aarch64.tar"是一个针对64位(aarch64架构)Linux系统的归档文件,我们需要对其进行解压并安装。 安装步骤如下: 1. **下载**:首先,你需要将JDK1.8的安装包下载到你的Linux系统中。这...

    java-jdk1.8-jdk-8u202-windows-x64.zip

    1. **下载**:从官方Oracle网站或其他可信赖的源下载`jdk-8u202-windows-x64.exe`安装程序。 2. **验证文件**:确保下载的文件完整且未被篡改,可以检查MD5或SHA校验和。 3. **双击安装**:在Windows x64系统上,...

    java-jdk1.8-jdk-8u181-windows-x64.zip

    这个压缩包"java-jdk1.8-jdk-8u181-windows-x64.zip"内包含两个文件:一个是主安装程序“jdk-8u181-windows-x64.exe”,用于在Windows 64位系统上安装JDK 1.8的更新181版本;另一个是“使用说明.txt”,通常会提供...

    jdk1.8可用的dubbo-admin-2.5.4.war

    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)

    MacOS M2 jdk1.8资源 jdk-8u381-macosx-aarch64.dmg

    总之,`jdk-8u381-macosx-aarch64.dmg`是专门为MacOS M2系统设计的JDK 1.8版本,提供了完整的开发环境,便于开发者在新的硬件平台上构建和运行Java应用。了解其安装、配置和主要特性对于提升开发效率至关重要。

    java-jdk1.8-jdk-8u201-windows-x64.zip

    安装JDK 1.8的过程非常简单,只需要双击下载的“jdk-8u201-windows-x64.exe”文件,然后按照安装向导的提示进行操作。通常,安装过程中会提供选择安装路径、是否设置环境变量等选项。为了确保Java环境的正确配置,...

    java-jdk1.8-jdk-8u191-linux-x64.zip

    这个压缩包文件“java-jdk1.8-jdk-8u191-linux-x64.zip”包含了用于在64位Linux系统上安装和使用的JDK 1.8更新191的所有必要组件。JDK(Java Development Kit)是开发和运行Java应用程序的基础,它包括了Java编译器...

    jdk1.8-windows-32位-免安装

    Java Development Kit(JDK)是Oracle公司提供的用于开发和运行Java应用程序的软件工具包。"jdk1.8-windows-32位-免安装"是指针对Windows操作系统,适用于32位系统的JDK 1.8版本,它具有无需安装的特点,用户只需将...

    java-jdk1.8-jdk-8u201-linux-x64.zip

    2. 这将解压出名为`jdk-8u201-linux-x64.tar.gz`的文件,接着使用`tar -zxvf jdk-8u80-linux-x64.tar.gz`将其解压到指定目录。 3. 配置环境变量。打开或创建`~/.bashrc`或`~/.bash_profile`文件,并添加以下行: ``...

    推荐一款JDK1.8版本非常好用-jdk-8u361-windows-x64

    JDK1.8安装文件安装即可,非常Nice~ 步骤1:下载JDK 首先,您需要把当前资源下载下来节约到指定目录 步骤2:安装JDK 一旦下载完成,您需要运行安装程序来安装JDK。如果您下载的是压缩包,需要解压缩到您希望安装...

    jdk1.8 32位官方正式版 jdk-8u91-windows-i586

    jdk1.8 32位官方正式版 jdk-8u91-windows-i586

    JDK 1.8 jdk-8u171-windows-x64

    JDK 1.8 jdk-8u171-windows-x64

    linux版jdk1.8 jdk-8u212-linux-x64.tar.gz

    Linux x64版本并下载jdk-8u212-linux-x64.tar.gz文件。 将下载的文件解压缩到您选择的目录中。 配置环境变量,使系统能够找到Java运行时环境。可以通过设置JAVA_HOME和PATH环境变量来实现。

    最后一版支持JDK1.8的eclipse eclipse-jee-2020-06-R-win32-x86_64

    2. Eclipse JEE版本:Eclipse提供了多种版本,其中JEE版本是专为Java企业级开发设计的,包含了一系列用于Web和企业应用开发的工具,如支持Java EE框架(如Spring、Struts等)、服务器集成(如Tomcat、GlassFish等)...

    java-jdk1.8-jdk-8u192-linux-x64.zip

    这个压缩包“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-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位官方正式版 jdk1.8 jdk-8u5-windows-i586 32位官方正式版

    java-jdk1.8-jdk-8u191-windows-x64.zip

    关于安装步骤,描述中提到“双击安装即可”,这意味着在Windows x64环境下,用户只需要下载`jdk-8u191-windows-x64.exe`这个安装文件,然后双击运行,按照安装向导的提示进行操作,选择安装路径,确认许可协议,最后...

Global site tag (gtag.js) - Google Analytics