当前位置:   article > 正文

JUC-atomic_juc atomic有什么用

juc atomic有什么用

jdk版本: 1.8

更多数据结构,算法,设计模式,源码分析等请关注我的微信公众号[技术寨],每周至少两篇优质文章

1.概念

juc的atomic是jdk1.5引入的,完整的包名是:java.util.concurrent.atomic,通过这些类可以帮助我们完成一些原子性的操作。不过它没有像synchronizd那样保证一段代码的原子性,它只能保证一个变量的原子性操作。synchronizd也能保证一个变量的原子性,为什么不用它呢?当然是性能问题,通过使用atomic包下的类来替代synchronizd,可以保证更好的性能。
口说无凭,我们来测试一下:


@BenchmarkMode(Mode.AverageTime)
@Warmup(iterations = 3, time = 1)
@Measurement(iterations = 5, time = 5)
@Threads(4)
@Fork(1)
@State(value = Scope.Benchmark)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public class AtomicTest {
    private int i = 0;
    private AtomicInteger atomicInteger = new AtomicInteger();


    @Benchmark
    public void synchronizedTest() {
        synchronized (Object.class) {
            i++;
        }
    }

    @Benchmark
    public void atomicTest() {
        atomicInteger.addAndGet(1);
    }

    public static void main(String[] args) throws RunnerException {
        Options opt = new OptionsBuilder()
                .include(AtomicTest.class.getSimpleName())
                .build();
        new Runner(opt).run();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

结果是:

Benchmark                    Mode  Cnt    Score    Error  Units
AtomicTest.atomicTest        avgt    5   70.215 ±  1.774  ns/op
AtomicTest.synchronizedTest  avgt    5  311.817 ± 28.141  ns/op
  • 1
  • 2
  • 3

我们将线程数调到40试试

Benchmark                    Mode  Cnt     Score     Error  Units
AtomicTest.atomicTest        avgt    5   685.087 ±  35.610  ns/op
AtomicTest.synchronizedTest  avgt    5  3057.262 ± 219.698  ns/op

  • 1
  • 2
  • 3
  • 4

结论: atomic类性能是synchronizd的4倍左右,所以如果要控制单个类的原子性修改记得要选择atomic类。

2. 原子类介绍

类名引入版本原子性操作变量类型说明
AtomicInteger1.5int保证整型变量原子性加减等操作
AtomicBoolean1.5boolean保证布尔变量原子性加减等操作,不过内部实现还是通过一个整型变量,1为ture,0为false
AtomicIntegerArray1.5int[]保证整型数组变量原子性加减等操作
AtomicIntegerFieldUpdater1.5int它和AtomicInteger作用是一样的,但是AtomicInteger是直接作用在它本身,而AtomicIntegerFieldUpdater可以指定类变量
AtomicLong1.5long参考AtomicInteger
AtomicLongArray1.5long[]参考AtomicIntegerArray
AtomicLongFieldUpdater1.5long参考AtomicIntegerFieldUpdater
AtomicReference1.5任何引用类型保证引用类型变量原子性更新等操作
AtomicReferenceArray1.5任何引用类型数组保证引用类型数组变量原子性更新等操作
AtomicReferenceFieldUpdater1.5任何引用类型参考AtomicIntegerArray
AtomicStampedReference1.5任何引用类型功能和AtomicReference一样,但是通过时间戳标记更新,解决了CAS的ABA问题
AtomicMarkableReference1.5任何引用类型功能和AtomicReference一样,但是通过布尔类型标记是否更新,解决了CAS的ABA问题,它比AtomicStampedReference更轻一点
LongAdder1.8long和AtomicLong功能相似,但是解决了cas在高并发下循环自旋的问题,不过代价就是结果不实时
LongAccumulator1.8longLongAdder是它的一种特例,它的功能更强大
DoubleAdder1.8double参考LongAdder
LongAccumulator1.8double参考LongAccumulator

3.相关知识简介

3.1 CAS

if(v == a){
   v = b;
}
  • 1
  • 2
  • 3

我们知道按照上面代码的写法,一定会存在并发问题。但是CPU帮我提供了一种底层方式保证这种操作的原子性,这就是CAS(Compare and swap)。它通过对比某一个内存地址的值之后再更新,每次只能有一个线程操作成功,从而保证其原子性。

3.2 volatile变量

在介绍volatile变量之前一定要介绍并发编程中三个非常重要的概念。

  • 原子性:一个操作要么成功要么失败
  • 可见性:一个线程修改之后其他线程能马上看到
  • 有序性:按照代码顺序执行
    volatile能保证的只是上面的两个特性:可见性和有序性。原子性并不能保证,比如:
volatile int v = 0;

public void volatileTest(){
    for (int i = 0; i < 100; i++) {
        new Thread(() -> {
            for(int j = 0 ; j < 1000; j++){
                v++;
            }
        }).start();
    } 
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

如果你执行上面的代码会发现,极大可能v的最终结果不是100000,原因就是volatile不能保证原子性。
可是,通过前面cas介绍,我们知道它可以保证原子性,而volatile又能保证可见性和有序性,所以它们就’双剑合璧’了,产生了我们看到的atomic类。

说明:这里只是简单介绍CAS和volatile变量,给大家一个直观的感受,后面会详细介绍jvm内存模型,CAS存在的问题等内容。

关注我的公众号,不迷路

公众号

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/运维做开发/article/detail/996108
推荐阅读
相关标签
  

闽ICP备14008679号