atomic

atomic(原子类)

  • 不可分割
  • 一个操作是不可中断的,即便是多线程的情况下也可以保证
  • java.util.concurrent.atomic
  • eTnrNEEOT原子类的作用和锁类似,是为了1保证开发情况下线桂女生。个个过原子类相比于锁,有一定的优势:
  • 粒度更细:原子变量可以把竞争范围缩小到变量级别这是我们可以获得的最细粒度的情况了,通常锁的粒度都要大于原子变量的粒度
  • 效率更高:通常,使用原子类的效率会比使用锁的效率更高除了高度竞争的情况

6类原子类

Atomic*基本类型原子类

Atomiclnteger

AtomicLong

AtomicBoolean

Atomic*Array数组类型原子类

AtomiclntegerArray、

AtomicLongArray、

AtomicReferenceArray

Atomic*Reference引用类型原子类

AtomicReference、

AtomicStampedReference、

AtomicMarkableReference

Atomic*FieldUpdater升级类型原子类

Atomiclntegerfieldupdater、

AtomicLongFieldUpdater、

AtomicReferenceFieldUpdater

Adder累加器

LongAdder、DoubleAdder

Accumulator累加器

LongAccumulator、

DoubleAccumulator

AtomicInteger

常用方法

public final int get() //获取当前的值

public final int getAndSet(int newValue) //获取当前的值,并设置新的值

public final int getAndIncrement() //获取当前的值,并自增

public final int getAndDecrement() //获取当前值,并自减

public final int getAndAdd(int delta) //获取当前值,并加上预期的值

bollean compareAndSet(int expect,int update) //如果输入的数值等于预期值,则以原子方式将该值设置为输入值

Atomic*Array

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 AtomicIntegerFieldUpdater<Candidate> scoreUpdater = AtomicIntegerFieldUpdater
.newUpdater(Candidate.class, "score");

@Override
public void run() {
for (int i = 0; i < 10000; i++) {
peter.score++;
scoreUpdater.getAndIncrement(tom);
}
}

public static class Candidate {

volatile int score;
}

public static void main(String[] args) throws InterruptedException {
tom=new Candidate();
peter=new Candidate();
AtomicIntegerFieldUpdaterDemo r = new AtomicIntegerFieldUpdaterDemo();
Thread t1 = new Thread(r);
Thread t2 = new Thread(r);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("普通变量:"+peter.score);
System.out.println("升级后的结果"+ tom.score);
}
}

注意点:

可见范围

不支持static

Adder累加器

高并发下LongAdder比AtomicLong效率高,不过本质是空间换时间。

竞争激烈时,LongAdder把不同线程对应到不同的Cell上进行修改,降低了冲突的概率,是多段锁的理念,提高了并发性。

多线程下AtomicLong的性能,每一次加法,都要flush和refresh,导致很耗费资源。

  • 在内部,这个LongAdder的实现原理和刚才的AtomicLong是有不同的,刚才的AtomicLong的实现原理是,每一次加法都需要做同步,所以在高并发的时候会导致冲突比较多,也就降低了效率。
  • 而此时的LongAdder,每个线程会有自己的一个计数器,仅用来在自己线程内计数,这样一来就不会和其他线程的计数器干扰。
  • 如图中所示,第一个线程的计数器数值,也就是ctr',为1的时候,可能线程2的计数器ctr'’|的数值已经是3了,他们之间并不存在竞争关系,所以在加和的过程中,根本不需要同步机制,也不需要刚才的flush和refresh。这里也没有一个公共的counter来给所有线程统一计数。

LongAdder引入了分段累加的概念,内部有一个base变量和一个Cell[]数组共同参与计数:

    • base变量:竞争不激烈,直接累加到该变量上
    • Cell[]数组:竞争激烈各个线程分散累加到自己的槽Cell[i]中

sum源码分析

public long sum(){

Cell[] as =cells;

Cell a;

long sum =base;

if (as !=null){

for (int i=0;i<as.length;++i){

if ((a =as))!=null)

sum +=a.value;

}

` }

return sum;

}

对比AtomicLong和LongAdder

  • 在低争用下,AtomicLong和LongAdder这两个类具有相似的特征。但是在竞争激烈的情况下,LongAdder的预期吞吐量要高得多,但要消耗更多的空间
  • LongAdder适合的场景是统计求和计数的场景,而且LongAdder基本只提供了add方法,而AtomicLong还具有cas方法

Accumulator累加器

与Adder非常相似,是更通用版本的Adder

public LongAccumulator(LongBinaryOperator accumulatorFunction, long identity)

@FunctionalInterface
public interface LongBinaryOperator {

/**
* Applies this operator to the given operands.
*
* @param left the first operand
* @param right the second operand
* @return the operator result
*/
long applyAsLong(long left, long right);
}

identity 赋值到applyAsLong函数参数left

accumulator.accumulate(i); i赋值到applyAsLong函数参数left;right为上次applyAsLong返回值

LongAccumulator accumulator = new LongAccumulator(LongAccumulatorDemo::applyAsLong, 1);

private static long applyAsLong(long x, long y) {
return 2 + x * y;
}

posted @ 2024-02-26 07:58  wangzhilei  阅读(28)  评论(0编辑  收藏  举报