概述
本文不讨论Atomic原子操作的底层原理与实现,仅针对其缺点进行讨论QaQ。
缺点
Atomic原子操作类实质上是一个CAS操作的实现,Atomic类的缺点实质上是CAS的缺点:
- 由于Atomic类CAS的实现中,存在do while的循环操作,在最恶劣的情况下,当前线程始终无法对比成功【预期值】,将会进行大量的循环,增加CPU的负荷,以getAndIncrement为例
public final int getAndAddInt(Object var1, long var2, int var4) {
int var5;
do {
var5 = this.getIntVolatile(var1, var2);
} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
return var5;
}
- Atomic类只能保证一个共享变量的原子操作
- 相对于synchronized可以对一段代码、一个方法进行加锁而言,Atomic的使用局限于某一个共享变量。
- ABA问题
- 当然,如果业务对中间过程不敏感,ABA问题就不会有什么影响。
package com.leolee.multithreadProgramming.juc.atomic;
import java.util.concurrent.atomic.AtomicReference;
/**
* @ClassName ABATest
* @Description: TODO
* @Author LeoLee
* @Date 2021/3/1
* @Version V1.0
**/
public class ABATest {
public static void main(String[] args) {
AtomicReference<Integer> integerAtomicReference = new AtomicReference<>(100);
int expect = integerAtomicReference.get();//100
//ABA问题
new Thread(() -> {
integerAtomicReference.compareAndSet(expect, 101);
integerAtomicReference.compareAndSet(integerAtomicReference.get(), 100);
}, "t1").start();
new Thread(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(integerAtomicReference.compareAndSet(expect, 102) + "\t" + integerAtomicReference.get());
}, "t2").start();
}
}
ABA问题解决
除了AtomicInteger、AtomicReference等,JUC还提供了AtomicStampedReference,带有时间戳的原子引用类,其原理类似于乐观锁的版本号控制。
package com.leolee.multithreadProgramming.juc.atomic;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.AtomicStampedReference;
/**
* @ClassName ABATest
* @Description: TODO
* @Author LeoLee
* @Date 2021/3/1
* @Version V1.0
**/
public class ABATest {
public static void main(String[] args) {
//ABA问题解决
AtomicStampedReference<Integer> integerAtomicStampedReference = new AtomicStampedReference<Integer>(100, 1);//初始值为100,版本号1
new Thread(() -> {
int stamp = integerAtomicStampedReference.getStamp();
System.out.println("t3获取的初始版本号为:" + stamp);
try {
Thread.sleep(1000);//保证t4线程可以获取到与此时t3一样的版本号
} catch (InterruptedException e) {
e.printStackTrace();
}
integerAtomicStampedReference.compareAndSet(100, 101, integerAtomicStampedReference.getStamp(), integerAtomicStampedReference.getStamp() + 1);//版本号+1
integerAtomicStampedReference.compareAndSet(101, 100, integerAtomicStampedReference.getStamp(), integerAtomicStampedReference.getStamp() + 1);//版本号+1
}, "t3").start();
new Thread(() -> {
int stamp = integerAtomicStampedReference.getStamp();
System.out.println("t4获取的初始版本号为:" + stamp);
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(integerAtomicStampedReference.compareAndSet(100, 102, stamp, stamp + 1) + "\t" + integerAtomicStampedReference.getReference());
}, "t4").start();
}
}
//测试结果:
t3获取的初始版本号为:1
t4获取的初始版本号为:1
false 100