JUC-原子操作类(AtomicLong, AtomicLongArray,AtomicReferren,AtomicLongFiledUpdater)LongAdder

1. 概述

多线程下使用原子类来保证线程安全

public class AtomicIntegerTest {

    AtomicInteger atomicInteger = new AtomicInteger(0);

    public int getNum(){
        return atomicInteger.get();
    }

    public void addNum(){
        atomicInteger.getAndIncrement();
    }
}

volatile能解决多线程并发安全中的可见性问题,但是不能解决原子性问题。
atomic原子类通过CAS+volatile来保证并发安全。CAS保证原子性,volatile保证原子性。

2. 分类

原子类位于java.util.concurrent.atomic包下面。可以分为以下几类,基本类型的原子类,数组类型的原子类,引用类型的原子类和对象的属性修改原子类四类。

2.1 基本类型原子类

AtomicBoolean, AtomicInteger, AtomicLong
常用的API:
获取值,自增,自减,CAS
在这里插入图片描述
使用举例:

public class AtomicIntegerDemo
{
    public static final int SIZE = 50;

    public static void main(String[] args) throws InterruptedException
    {
        MyNumber myNumber = new MyNumber();
        CountDownLatch countDownLatch = new CountDownLatch(SIZE);

        for (int i = 1; i <=SIZE; i++) {
            new Thread(() -> {
                try {
                    for (int j = 1; j <=1000; j++) {
                        myNumber.addPlusPlus();
                    }
                } finally {
                    countDownLatch.countDown();
                }
            },String.valueOf(i)).start();
        }
        //等待上面50个线程全部计算完成后,再去获得最终值

        //暂停几秒钟线程
        //try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); }

        countDownLatch.await();

        System.out.println(Thread.currentThread().getName()+"\t"+"result: "+myNumber.atomicInteger.get());
    }
}

补充:CountDownLatch计数器,可以使一个或多个线程等待其他线程执行完毕后再执行。
CountDownLatch定义了一个计数器和阻塞队列,当计数器减为0就唤醒阻塞队列中的线程。使用await()方法主动将当前线程阻塞(放入阻塞队列中)

2.2 数组类型原子类

AtomicIntegerArray, AtomicLongArray, AtomicReferenceArray
常用的API

getAndSet(int i, int newValue)
getAndIncrement(int i)
getAndAdd(int i, int delta)

2.3 引用类型原子类

AtomicReference
原子引用类
AtomicStampedReference
原子邮戳(版本号)引用类,携带版本号的的原子引用。
AtomicMarkableReference
原子标记引用类,带标记(将版本号简化为true,false的标记)的原子引用。
常用API

public boolean isMarked();
public boolean compareAndSet(V       expectedReference,
                                 V       newReference,
                                 boolean expectedMark,
                                 boolean newMark)

2.4 对象的属性修改原子类

AtomicIntegerFiledUpdater, AtomicLongFiledUpdater, AtomicReferenceFiledUpdater
基于反射实现的,可以对volatile属性进行更新。因此需要更新的属性必须用volatile修饰。(volatile重要用法)
常用的API

static AtomicIntegerFieldUpdater<U> newUpdater(Class<U> tclass,String fieldName) 
public int getAndIncrement(T obj) 
static AtomicReferenceFieldUpdater<U,W> newUpdater(Class<U> tclass,Class<W> vclass,String fieldName)//如果是引用属性,需要传入引用属性的字节码对象

举例: AtomicIntegerFiledUpdater

class BankAccount//资源类
{
    String bankName = "CCB";

    //更新的对象属性必须使用 public volatile 修饰符。
    public volatile int money = 0;//钱数

    public void add()
    {
        money++;
    }

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

    AtomicIntegerFieldUpdater<BankAccount> fieldUpdater =
            AtomicIntegerFieldUpdater.newUpdater(BankAccount.class,"money");

    //不加synchronized,保证高性能原子性,局部微创小手术
    public void transMoney(BankAccount bankAccount)
    {
        fieldUpdater.getAndIncrement(bankAccount);
    }


}

举例: AtomicReferenceFiledUpdater

class MyVar //资源类
{
    public volatile Boolean isInit = Boolean.FALSE;

    AtomicReferenceFieldUpdater<MyVar,Boolean> referenceFieldUpdater =
            AtomicReferenceFieldUpdater.newUpdater(MyVar.class,Boolean.class,"isInit");

    public void init(MyVar myVar)
    {
        if (referenceFieldUpdater.compareAndSet(myVar,Boolean.FALSE,Boolean.TRUE))
        {
            System.out.println(Thread.currentThread().getName()+"\t"+"----- start init,need 2 seconds");
            //暂停几秒钟线程
            try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
            System.out.println(Thread.currentThread().getName()+"\t"+"----- over init");
        }else{
            System.out.println(Thread.currentThread().getName()+"\t"+"----- 已经有线程在进行初始化工作。。。。。");
        }
    }
}

3. 原子操作增强类LongAdder, LongAccumulator

在高并发的情况下,使用Atomic原子类会导致线程竞争的时候CAS操作自旋消耗CPU的性能,这个时候用LongAdder性能更好,代价是空间消耗更多,以空间换时间。
LongAdder只能加减法,且从0开始。
LongAccumulator更强大,可以自定义运算。

3.1 LongAdder为什么这么快?

在低并发无竞争的情况下跟AtomicLong一样,对同一个base操作。
当高并发的情况下:
LongAdder的核心思想就是以空间换时间,分散热点。在Atomic原子类的基础上将value值分散到多个cell中去(cell数组),不同的线程会命中不同的cell,每个线程对自己对应的cell进行操作,减少冲突从而减少CAS自旋。最后将各个celll中的变量累加返回即可。
在这里插入图片描述

3.2 LongAdder使用

常用的API
在这里插入图片描述
示例:

public class LongAdderAPIDemo
{
    public static void main(String[] args)
    {
        LongAdder longAdder = new LongAdder();
        longAdder.increment();
        longAdder.increment();
        longAdder.increment();
        System.out.println(longAdder.sum());
        LongAccumulator longAccumulator = new LongAccumulator(new LongBinaryOperator()
        {
            @Override
            public long applyAsLong(long left, long right)
            {
                return left + right;
            }
        },0);
        longAccumulator.accumulate(1);//1
        longAccumulator.accumulate(3);//4
        System.out.println(longAccumulator.get());
    }
}

4. 原子操作总结

AtomicLong: 原理CAS+自旋+volatile。进行计数,能够保证并发安全。在高并发下,性能急剧下降,自旋操作影响性能。
LongAdder: 原理CAS+Base+Cell+volatile。进行计数,能够保证并发安全。在高并发下,性能也很好。但是空间消耗略大。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值