• 原子操作类


    基础类型原子类

    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) + " 毫秒" + "	 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) + " 毫秒" + "	 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) + " 毫秒" + "	 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) + " 毫秒" + "	 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);
        }
    }
    

    阿里手册

  • 相关阅读:
    黑客书架上的书籍(转)
    vc 得到文件后缀名(转)
    配置IIS7(转)
    vs2008 目标框架 发布遇到的问题(转)
    CListCtrl用法(转)
    T400 折腾
    VS2008和.NET Framework3.5新功能(转)
    sql 2008 ctp 安装
    关于定位lsass内存中的明文密码(转)
    NT系统下木马进程的隐藏与检测(转)
  • 原文地址:https://www.cnblogs.com/yerikm/p/15415723.html
Copyright © 2020-2023  润新知