JUC原子类保证了对于添加、递增等可以进行原子性操作,用于并发编程中保证线程安全。
JUC原子类
优势
原子类的作用和锁类似,用于保证并发情况下线程安全,相比于锁,有一定的优势。
- 粒度更细:原子变量可以把竞争范围缩小到变量级别
- 效率更高:除了高度竞争的情况之外,使用原子类的效率通常会比使用同步互斥锁的效率更高,因为原子类底层利用了 CAS 操作,不会阻塞线程。
6大原子类
Atomic*基本类型原子类
包括三种,分别是 AtomicInteger
、AtomicLong
和 AtomicBoolean
。AtomicInteger。AtomicInteger是对int类型的封装,并且提供了原子性的访问和更新。因此需要一个用在并发场景下的整型变量时,可以直接使用它。
常用方法
public final int get() //获取当前的值
public final int getAndIncrement() //获取当前的值,并自增
public final int getAndDecrement() //获取当前的值,并自减
public final int getAndAdd(int delta) //获取当前的值,并加上预期的值
boolean compareAndSet(int expect, int update) //如果输入的数值等于预期值,则以原子方式将该值更新为输入值(update)
存在的问题
现假设场景为:添加一个自增一次的任务,执行100次
在AtomicLong
中,用来保存数值的value属性被volatile修饰,需要保证自身可见性。即每次数值有变化就要进行flush和reflush,才能保证其他线程感知到数值的变化。竞争激烈时,这两步操作会耗费大量资源。
改进
在 JDK 8 中又新增了 LongAdder
这个类,这是一个针对 Long 类型的操作工具类。
/**
* 描述: 在16个线程下使用LongAdder
*/
public class LongAdderDemo {
public static void main(String[] args throws InterruptedException {
//AtomicLong counter = new AtomicLong(0);
LongAdder counter = new LongAdder();
ExecutorService service = Executors.newFixedThreadPool(16);
for (int i = 0; i < 100; i++) {
service.submit(new Task(counter));
}
Thread.sleep(2000);
//System.out.println(counter.get())
System.out.println(counter.sum());
}
static class Task implements Runnable {
//private final AtomicLong counter;
private final LongAdder counte;
public Task(LongAdder counter) {
this.counter = counter;
@Override
public void run() //counter.incrementAndGet();
counter.increment();
}
}
}
- LongAdder引入了分段累加的概念,内部一共有两个参数参与计数:第一个叫作base,它是一个变量,第二个是
Cell[ ]
,是一个数组。其中的base是用在竞争不激烈的情况下的,可以直接把累加结果改到base变量上。一旦竞争激烈,各个线程会分散累加到自己所对应的那个Cell[]数组的某一个对象中,而不会大家共用同一个,本质是通过空间换时间 降低冲突的概率。 - 最后一步的求和sum方法,执行
LongAdder.sum()
的时候,会把各个线程里的Cell累计求和,并加上base,形成最终的总和。
Array数组类型原子类
数组里的元素,都可以保证其原子性,一共分为三种:AtomicIntegerArray,AtomicLongArray,AtomicReferenceArray。
引用类型原子类
AtomicReference
引用类型原子类,AtomicReference 可以让一个对象保证原子性。
原子更新器
使用原子更新器可以将已经声明的变量进行升级,使其具有原子性。
- AtomicIntegerFieldUpdater:原子更新整形的更新器;
- AtomicLongFieldUpdater:原子更新长整形的更新器;
- AtomicReferenceFieldUpdater:原子更新引用的更新器。
Adder 加法器
Accumulator 积累器
AtomicInteger和synchronized
区别
- 原理不同:synchronized利用的是
monitor
锁,原子类利用的是CAS
操作保证线程安全; - 使用范围不同:原子类仅仅是一个对象,而synchronized可以修饰方法,也可以修饰代码块;
- 粒度不同:原子类的粒度更小;
- 性能:synchronized是
悲观锁
,原子类是乐观锁
,悲观锁的开销是固定的,乐观锁的开销是逐步增长的。