原子类
内容纲要
原子类简介
- 不可分割
- 一个操作是不可中断的, 即便是多线程的情况下也可以保证
- java.util.concurrent.atomic
- 原子类的作用和锁类似, 是为了保证并发情况下线程安全。不过原子类相比锁,有一定的优势
- 颗粒度更细:原子变量可以把竞争范围缩小到变量级别,这是我们可以获得最细颗粒度的情况了,通常锁的颗粒度要大于原子变量的粒度
- 效率更高:通常,使用原子类的效率会比使用锁的效率更高,除了高度竞争的情况
Atomic*基本类型原子
- AtomicInteger: 整形原子类
- AtomicLong: 长整型原子类
- AtomicBoolean :布尔型原子类
AtomicInteger常用方法
- public finalint get() //获取当前的值
- public finalint getAndSet(int newValue)//获取当前的值并设置新的值imooc
- public finalint getAndIncrement()//获取当前的值,并自增
- public finalint getAndDecrement() //获取当前的值,并自减
- public finalint getAndAdd(int delta) //获取当前的值,并加上预期的值
- boolean compareAndSet(int expect, int update) //如果输入的数值等于预期值,则以原子方式将该值设置为输入值( update )
Atomic*Reference引用类型原子类
- AtomicReference:AtomicReference类的作用,和AtomicInteger并没有本质区别,AtomicInteger可以让一个整数保证原子性,而AtomicReference可以让一个对象保证原子性,当然,AtomicReference的功能明显比AtomicInteger强,因为一个对象里可以包含很多属性。用法和AtomicInteger类似。
把普通变量升级为具有原子功能
- AtomicIntegerFieldUpdater对普通变量进行升级
- 使用场景: 偶尔需要一个原子get-set操作
public class AtomicIntegerFieldUpdaterDemo implements Runnable{
static Candidate tom;
static Candidate peter;
public static final AtomicIntegerFieldUpdater<Candidate> scoreUpdater = AtomicIntegerFieldUpdater.newUpdater(Candidate.class,"score");
@Override
public void run() {
for (int i = 0; i < 100000; i++) {
peter.score ++;
scoreUpdater.getAndIncrement(tom);
}
}
public static class Candidate{
volatile int score =0 ;
}
public static void main(String[] args) throws InterruptedException {
tom = new Candidate();
peter = new Candidate();
AtomicIntegerFieldUpdaterDemo r = new AtomicIntegerFieldUpdaterDemo();
Thread thread = new Thread(r);
thread.start();
Thread thread1 = new Thread(r);
thread1.start();
thread.join();
thread1.join();
System.out.println("tom:"+tom.score);
System.out.println("peter:"+peter.score);
}
}
注意点
- 可见范围
- 底层使用反射
- 不支持static修饰的变量
Adder累加器
- 是Java8引入的,相对是比较新的一个类
- 高并发下LongAdder比AtomicLong效率高,不过本质是空间换时间
- 竞争激烈的时候,LongAdder把不同线程对应到不同的Cell 上进行修改,降低了冲突的概率,是多段锁(ConcurrentHashMap在1.7中也是使用多段锁,1.8改为node) 的理念,提高了并发性
AtomicLong 弊端
LongAdder带来的改进和原理
- 在内部,这个LongAdder的实现原理和刚才的AtomicLong是有不同的,刚才的AtomicLong的实现原理是,每一次加法都需要做同步,所以在高并发的时候会导致冲突比较多,也就降低了效率
- 而此时的LongAdder,每个线程会有自己的一个计数器,仅用来在自己线程内计数,这样一来就不会和其他线程的计数器干扰
- 如上图中所示,第一个线程的计数器数值,也就是ctr,为1的他们之时候,可能线程2的计数器ctr’’的数值已经是3了,间并不存在竞争关系,所以在加和的过程中,根本不需要同步机制,也不需要刚才的flush和refresh。这里也没有一个公共的counter来给所有线程统一计数
- LongAdder引入了分段累加的概念,内部有一个base变量和个CelII数组共同参与计数
- base变量:竞争不激烈,直接累加到该变量上
- CellII数组:竞争激烈,各个线程分散累加到自己的槽Cellril中
- sum 源码
public long sum() {
Striped64.Cell[] cs = this.cells;
long sum = this.base;
if (cs != null) {
Striped64.Cell[] var4 = cs;
int var5 = cs.length;
for(int var6 = 0; var6 < var5; ++var6) {
Striped64.Cell c = var4[var6];
if (c != null) {
sum += c.value;
}
}
}
return sum;
}
对比AtomicLong和LongAdder
- 在低争用下,AtomicLong和LongAdder这两个类具有相似的特征。但是在竞争激烈的情况下,LongAdder的预期吞吐量要高得多,但要消耗更多的空间
- LongAdder适合的场景是统计求和计数的场景,而且LongAdder基本只提供了add方法,而AtomicLong还具有cas方法
Accumulator累加器
- Accumulator和Adder非常相似,Accumulator就是一个更通用版本的Adder
共有 0 条评论