我是靠谱客的博主 光亮大叔,最近开发中收集的这篇文章主要介绍JAVA并发-原子类/加法器(Adder)和累加器(Accumulator),觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

文章目录

    • 一、原子类基础
      • 1. 什么是java原子类?
      • 2. demo测试,不使用原子类测试
        • 2.1 i++是否原子操作?并解释为什么?
      • 3. 原子类使用场景
        • 3.1 原子类如何使用
    • 二、加法器(Adder)和累加器(Accumulator)
      • 1. java8中为什么要新增LongAdder?
      • 2. LongAdder
        • 2.1 LongAdder原理
      • 3. Accumulator
    • 三、参考

一、原子类基础

1. 什么是java原子类?

Java中的原子类-并发编程
参考URL: https://blog.csdn.net/balsamspear/article/details/86575173

原子类包装了一个变量,然后提供对这个变量的原子操作的方法。

注意:原子类中对变量的操作,都是原子操作。
JDK在1.5时提供了很多原子类,在java.util.concurrent.atomic包下。

2. demo测试,不使用原子类测试

/**
 * 这个例子很简单,起20个线程,每个线程执行1万次i++操作,预期结果应该是20万,实际结果却是7万多。
 * 原因是,虽然volatile保证了有序性和可见性,但是不能保证原子性,i++不是原子操作。
 */
public class AtomicTest {

    private static volatile int i;

    private static void increase() {
        i++;
    }

    public static void main(String[] args) throws Exception {
        Thread[] threads = new Thread[20];
        for (int k = 0; k < 20; k++) {
            threads[k] = new Thread(() -> {
                for (int j = 0; j < 10000; j++) {
                    increase();
                }
            });
            threads[k].start();
        }

        for (Thread thread : threads) {
            while (thread.isAlive()) {
                Thread.sleep(100);
            }
        }
        System.out.println(i);
    }
}
  • 这个例子很简单,起20个线程,每个线程执行1万次i++操作,预期结果应该是20万,实际结果却是7万多。
  • 原因是,虽然volatile保证了有序性和可见性,但是不能保证原子性,i++不是原子操作。

2.1 i++是否原子操作?并解释为什么?

volatile为什么不能保证原子性
参考URL: https://blog.csdn.net/xdzhouxin/article/details/81236356

对于i++这样的操作,其实是分3步执行的,读取i的值,增加i的值,回写i的新值。这3步每一步都是原子操作,但是组合在一起就不一定是原子操作了。

3. 原子类使用场景

对于是需要简单的递增或者递减的需求场景,使用synchronized关键字和lock固然可以实现,但代码写的会略显冗余,且性能会有影响,此时用原子类更加方便。

3.1 原子类如何使用

上面介绍了原子类有4大类,这里以原子更新基本类型中的AtomicInteger类为例,介绍通用的API接口和使用方法。

首先是几个常用的API:

// 以原子方式将给定值与当前值相加,可用于线程中的计数使用,(返回更新的值)。
int addAndGet(int delta)

// 以原子方式将给定值与当前值相加,可用于线程中的计数使用,(返回以前的值)
int getAndAdd(int delta)

// 以原子方式将当前值加 1(返回更新的值)
int incrementAndGet()

// 以原子方式将当前值加 1(返回以前的值)
int getAndIncrement() 

// 以原子方式设置为给定值(返回旧值)
int getAndSet(int newValue)

// 以原子方式将当前值减 1(返回更新的值)
int decrementAndGet()// 以原子方式将当前值减 1(返回以前的值)
int getAndDecrement()

// 获取当前值
get()

二、加法器(Adder)和累加器(Accumulator)

加法器(Adder)和累加器(Accumulator):原子类型的扩充与优化,主要有:LongAdder、LongAccumulator、DoubleAdder和DoubleAccumulator,比AtomicLong和AtomicDouble性能更优。

1. java8中为什么要新增LongAdder?

大家对AtomicInteger的基本实现机制应该比较了解,它们是在一个死循环内,不断尝试修改目标值,知道修改成功,如果竞争不激烈,那么修改成功的概率就很高,否则,修改失败的概率就很高,在大量修改失败时,这些原子操作就会进行多次循环尝试,因此性能就会受到影响。

​ 在 JDK 8 中又新增了 LongAdder 这个类,这是一个针对 Long 类型的操作工具类。那么既然已经有了 AtomicLong,为何又要新增 LongAdder 这么一个类呢?

LongAdder是java8中新增的原子类,在多线程环境中,它比AtomicLong性能要高出不少,特别是写多的场景。

经过试验,LongAdder 的吞吐量大约是 AtomicLong 的十倍,不过凡事总要付出代价,LongAdder 在保证高效的同时,也需要消耗更多的空间。

2. LongAdder

​ 在 JDK 8 中又新增了 LongAdder 这个类,这是一个针对 Long 类型的操作工具类。

2.1 LongAdder原理

死磕 java并发包之LongAdder源码分析
参考URL: https://zhuanlan.zhihu.com/p/65520633

LongAdder的原理是,在最初无竞争时,只更新base的值,当有多线程竞争时通过分段的思想,让不同的线程更新不同的段,最后把这些段相加就得到了完整的LongAdder存储的值。

在这里插入图片描述

3. Accumulator

Accumulator 和 Adder 非常相似,实际上 Accumulator 就是一个更通用版本的 Adder,比如 LongAccumulator 是 LongAdder 的功能增强版,因为 LongAdder 的 API 只有对数值的加减,而 LongAccumulator 提供了自定义的函数操作。

三、参考

java fork-join框架应用和分析
参考URL: https://www.iteye.com/blog/shmilyaw-hotmail-com-1897636
Fork/Join框架(一)引言
参考URL: https://ifeve.com/fork-join-1/
Fork/Join框架基本使用
参考URL: https://blog.csdn.net/tyrroo/article/details/81390202
ForkJoin框架使用和原理剖析
参考URL: https://blog.csdn.net/codingtu/article/details/88729498

最后

以上就是光亮大叔为你收集整理的JAVA并发-原子类/加法器(Adder)和累加器(Accumulator)的全部内容,希望文章能够帮你解决JAVA并发-原子类/加法器(Adder)和累加器(Accumulator)所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(48)

评论列表共有 0 条评论

立即
投稿
返回
顶部