Atomic包常用类总结

1. 前言

Atomic是JUC提供的一组原子操作的封装类,它们位于java.util.concurrent.atomic中,Atomic类是通过无锁(lock-free)的方式实现的线程安全(thread-safe)访问。它的主要原理是利用了CAS(Compare and Set),其主要包括以下几部分:

原子方式更新普通类型:
AtomicBoolean:原子更新布尔类型。
AtomicInteger:原子更新整型。
AtomicLong / LongAdder / LongAccumulator:原子更新长整型
DoubleAdder / DoubleAccumulator :原子更新双精度整型

原子方式更新数组:
AtomicIntegerArray:原子更新整型数组里的元素。
AtomicLongArray:原子更新长整型数组里的元素。
AtomicReferenceArray:原子更新引用类型数组里的元素

原子方式更新引用,与其它不同的是,更新引用可以更新多个变量,而不是一个变量;
AtomicReference:原子更新引用类型。
AtomicReferenceFieldUpdater:原子更新引用类型里的字段。
AtomicMarkableReference:原子更新带有标记位的引用类型。

原子方式更新字段,
AtomicIntegerFieldUpdater:原子更新整型字段的更新器。
AtomicLongFieldUpdater:原子更新长整型字段的更新器。
AtomicStampedReference:原子更新带有版本号的引用类型,用于解决使用CAS进行原子更新时,可能出现的ABA问题。

Atomic

2. 原子更新普通类型

2.1. AtomicInteger

AtomicInteger提供的主要操作有

增加值并返回新值:int addAndGet(int delta)
加1后返回新值:int incrementAndGet()
获取当前值:int get()
用CAS方式设置:int compareAndSet(int expect, int update)

通过运行下面程序,创建10个线程,每个线程循环100次incrementAndGet()操作,输出预期结果1000,证明Atomic确实可以保证线程安全

public class D01_AtomicInteger {

    AtomicInteger count = new AtomicInteger(0);

     void m1() {
        for (int i = 0; i < 100; i++) {
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            count.incrementAndGet(); 
        }
    }

    public static void main(String[] args) {
        D01_AtomicInteger t = new D01_AtomicInteger();
        List threads = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            threads.add(new Thread(t::m1,"thread" + i));
        }

        threads.forEach((o) -> o.start());

        threads.forEach(o -> {
            try {
                o.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        System.out.println(t.count);  // 1000
    }
}

2.2. LongAdder

LongAdder是一个多线程高并发时使用的自增计数器,它的设计思想就是以空间换时间。LongAdderAtomicLong的区别在于高并发时LongAdder将对单一变量的CAS操作分散为对数组cells中多个元素的CAS操作,取值时进行求和;而在并发较低时仅对base变量进行CAS操作,与AtomicLong类原理相同:

AtomicLong

当前值加1:·void increment()`
获取当前值:long longValue()

下面程序演示了在高并发情况下,AtomicLong和LongAdder的效率差别:

public class AtomicVsLongAdder {
    static AtomicLong count1 = new AtomicLong(0L);
    static LongAdder count2 = new LongAdder();


    static void testAtomic(Thread[] threads) throws InterruptedException {


        for(int i=0; i {
                        for(int k=0; k<300000; k++) count1.incrementAndGet();
                    });
        }

        long start = System.currentTimeMillis();

        for(Thread t : threads ) t.start();

        for (Thread t : threads) t.join();

        long end = System.currentTimeMillis();

        System.out.println("Atomic:" + count1.get() + ", 程序运行时间:" + (end-start) + "ms");
    }

    static void testLongAdder(Thread[] threads) throws InterruptedException {
        for(int i=0; i {
                        for(int k=0; k<300000; k++) count2.increment();
                    });
        }

        long start = System.currentTimeMillis();

        for(Thread t : threads ) t.start();

        for (Thread t : threads) t.join();

        long end = System.currentTimeMillis();

        System.out.println("LongAdder:" + count2.longValue() + ", 程序运行时间:" + (end-start) + "ms");
    }

    public static void main(String[] args) throws Exception {
        Thread[] threads1 = new Thread[5000];
        Thread[] threads2 = new Thread[5000];

        testAtomic(threads1); // Atomic:1500000000, 程序运行时间:11347ms
        testLongAdder(threads2); // LongAdder:1500000000, 程序运行时间:8978ms
    }
}

2.3. LongAccumulator

LongAccumulator是LongAdder的扩展。LongAdder的API只有对数值的加减,而LongAccumulator提供了自定义的函数操作。其构造函数如下:

 // accumulatorFunction:需要执行的二元函数(接收2个long作为形参,并返回1个long);identity:初始值
public LongAccumulator(LongBinaryOperator accumulatorFunction, long identity) {
  this.function = accumulatorFunction;
  base = this.identity = identity;
}

以下代码可以看出,accumulate(value)传入的值会与上一次的比较值对比,然后保留较大者,最后打印出最大值。

public class LongAccumulatorDemo{
    public static void main(String[] args) throws InterruptedException {
        LongAccumulator accumulator = new LongAccumulator(Long::max, Long.MIN_VALUE);
        Thread[] ts = new Thread[1000];

        for (int i = 0; i < 1000; i++) {
            ts[i] = new Thread(() -> {
                Random random = new Random();
                long value = random.nextLong();
                accumulator.accumulate(value); // 比较value和上一次的比较值,然后存储较大者
            });
            ts[i].start();
        }
        for (int i = 0; i < 1000; i++) {
            ts[i].join();
        }
        System.out.println(accumulator.longValue());
    }
}

你可能感兴趣的:(Atomic包常用类总结)