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
:原子更新整型字段的更新器。
AtomicLongFieldUpdate
r:原子更新长整型字段的更新器。
AtomicStampedReference
:原子更新带有版本号的引用类型,用于解决使用CAS进行原子更新时,可能出现的ABA问题。
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<Thread> 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
是一个多线程高并发时使用的自增计数器,它的设计思想就是以空间换时间。LongAdder
与AtomicLong
的区别在于高并发时LongAdder
将对单一变量的CAS操作分散为对数组cells中多个元素的CAS操作,取值时进行求和;而在并发较低时仅对base变量进行CAS操作,与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<threads.length; i++) {
threads[i] =
new Thread(()-> {
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<threads.length; i++) {
threads[i] =
new Thread(()-> {
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());
}
}