原子操作类

发布时间:2022-07-02 发布网站:脚本宝典
脚本宝典收集整理的这篇文章主要介绍了原子操作类脚本宝典觉得挺不错的,现在分享给大家,也给大家做个参考。

基础类型原子类

AtomicInteger,AtomicBoolean,AtomicLong。底层都是CAS,会出现ABA问题。

常用API:

API 说明
public final int get() 获取当前的值
public final int getAndSet(int newValue) 获取当前的值,并设置新的值
public final int getAndIncrement() 获取当前的值,并自增
public final int getAndDecrement() 获取当前的值,并自减
public final int getAndAdd(int delta) 获取当前的值,并加上预期的值
boolean compareAndSet(int expect, int update) 如果输入的数值等于预期值,则以原子方式将该值设置为输入值(update)

数组类型原子类

AtomicIntegerArray,AtomicLongArray,AtomicReferenceArray

引用类型原子类

AtomicReference(可以做一个简单的自旋锁),AtomicStamPEdReference(可以解决ABA问题),AtomicMarkableReference(可以判断是否修改过)

对象的属性修改原子类

AtomicIntegerFieldUpdater,AtomicLongFieldUpdater,AtomicReferenceFieldUpdater

作用:以一种线程安全的方式操作非线程安全对象内的某些字段。

强制:1.更新的对象属性必须使用 public volatile 修饰符;2.因为对象的属性修改类型原子类都是抽象类,所以每次使用都必须 使用静态方法newUpdater()创建一个更新器,并且需要设置想要更新的类和属性。

原子操作增强类

LongAdder,DoubleAdder:只能用来计算加法 ,且从零开始计算

LongAccumulator,DoubleAccumulator:LongAccumulator提供了自定义的函数操作

LongAdder

LongAdder的基本思路就是分散热点,将value值分散到一个Cell数组中,不同线程会命中到数组的不同槽中,各个线程只对自己槽中的那个值进行CAS操作,这样热点就被分散了,冲突的概率就小很多。如果要获取真正的long值,只要将各个槽中的变量值累加返回。 sum()会将所有Cell数组中的value和base累加作为返回值,核心的思想就是将之前AtomicLong一个value的更新压力分散到多个value中去,从而降级更新热点。Value = Base + Cell的值。

LongAdder和AtomicLong对比

AtomicLong LongAdder
原理 CAS+自旋 CAS+Base+Cell数组分散(分而治之),空间换时间并分散了热点数据
使用场景 低并发下的全局计算且计数准确 高并发下的全局计算
缺点 高并发后性能急剧下降,自旋会成为瓶颈 sum求和后还有计算线程修改结果的话,最后结果不够准确,只能保证最终一致性

性能测试

package com.example.juc;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.LongAccumulator;
import java.util.concurrent.atomic.LongAdder;

/**
 * @author yeric
 * @description: LongAdder是jdk1.8提供的累加器,基于Striped64实现。它常用于状态采集、统计等场景。AtomicLong也可以用于这种场景,但在线程竞争激烈的情况下,LongAdder要比AtomicLong拥有更高的吞吐量,但会耗费更多的内存空间。
 * LongAccumulator和LongAdder类似,也基于Striped64实现。但要比LongAdder更加灵活(要传入一个函数式接口),LongAdder相当于是LongAccumulator的一种特例。
 * @date 2021/10/11 22:24
 */
public class LongAdderDemo {
    public static void main(String[] args) throws InterruptedException {
        ClickNumberNet clickNumberNet = new ClickNumberNet();
        long startTime;
        long endTime;
        CountDownLatch countDownLatch = new CountDownLatch(50);
        CountDownLatch countDownLatch2 = new CountDownLatch(50);
        CountDownLatch countDownLatch3 = new CountDownLatch(50);
        CountDownLatch countDownLatch4 = new CountDownLatch(50);
        startTime = System.currentTimeMillis();
        for (int i = 1; i <= 50; i++) {
            new Thread(() -> {
                try {
                    for (int j = 1; j <= 100 * 10000; j++) {
                        clickNumberNet.clickBySync();
                    }
                } finally {
                    countDownLatch.countDown();
                }
            }, String.valueOf(i)).start();
        }
        countDownLatch.awaIT();
        endTime = System.currentTimeMillis();
        System.out.PRintln("----costTime: " + (endTime - startTime) + " 毫秒" + "t clickBySync result: " + clickNumberNet.number);
        startTime = System.currentTimeMillis();
        for (int i = 1; i <= 50; i++) {
            new Thread(() -> {
                try {
                    for (int j = 1; j <= 100 * 10000; j++) {
                        clickNumberNet.clickByAtomicLong();
                    }
                } finally {
                    countDownLatch2.countDown();
                }
            }, String.valueOf(i)).start();
        }
        countDownLatch2.await();
        endTime = System.currentTimeMillis();
        System.out.println("----costTime: " + (endTime - startTime) + " 毫秒" + "t clickByAtomicLong result: " + clickNumberNet.atomicLong);
        startTime = System.currentTimeMillis();
        for (int i = 1; i <= 50; i++) {
            new Thread(() -> {
                try {
                    for (int j = 1; j <= 100 * 10000; j++) {
                        clickNumberNet.clickByLongAdder();
                    }
                } finally {
                    countDownLatch3.countDown();
                }
            }, String.valueOf(i)).start();
        }
        countDownLatch3.await();
        endTime = System.currentTimeMillis();
        System.out.println("----costTime: " + (endTime - startTime) + " 毫秒" + "t clickByLongAdder result: " + clickNumberNet.longAdder.sum());
        startTime = System.currentTimeMillis();
        for (int i = 1; i <= 50; i++) {
            new Thread(() -> {
                try {
                    for (int j = 1; j <= 100 * 10000; j++) {
                        clickNumberNet.clickByLongAccumulator();
                    }
                } finally {
                    countDownLatch4.countDown();
                }
            }, String.valueOf(i)).start();
        }
        countDownLatch4.await();
        endTime = System.currentTimeMillis();
        System.out.println("----costTime: " + (endTime - startTime) + " 毫秒" + "t clickByLongAccumulator result: " + clickNumberNet.longAccumulator.longValue());
    }
}

class ClickNumberNet {
    int number = 0;

    public synchronized void clickBySync() {
        number++;
    }

    AtomicLong atomicLong = new AtomicLong(0);

    public void clickByAtomicLong() {
        atomicLong.incrementAndGet();
    }

    // LongAdder只能用来计算加法,且从零开始计算
    LongAdder longAdder = new LongAdder();

    public void clickByLongAdder() {
        longAdder.increment();
    }

    // LongAccumulator提供了自定义的函数操作
    LongAccumulator longAccumulator = new LongAccumulator((x, y) -> x + y, 0);

    public void clickByLongAccumulator() {
        longAccumulator.accumulate(1);
    }
}

原子操作类

阿里手册

原子操作类

脚本宝典总结

以上是脚本宝典为你收集整理的原子操作类全部内容,希望文章能够帮你解决原子操作类所遇到的问题。

如果觉得脚本宝典网站内容还不错,欢迎将脚本宝典推荐好友。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。