赞
踩
jdk版本: 1.8
更多数据结构,算法,设计模式,源码分析等请关注我的微信公众号[技术寨],每周至少两篇优质文章
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(); } }
结果是:
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
我们将线程数调到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
结论: atomic类性能是synchronizd的4倍左右,所以如果要控制单个类的原子性修改记得要选择atomic类。
类名 | 引入版本 | 原子性操作变量类型 | 说明 |
---|---|---|---|
AtomicInteger | 1.5 | int | 保证整型变量原子性加减等操作 |
AtomicBoolean | 1.5 | boolean | 保证布尔变量原子性加减等操作,不过内部实现还是通过一个整型变量,1为ture,0为false |
AtomicIntegerArray | 1.5 | int[] | 保证整型数组变量原子性加减等操作 |
AtomicIntegerFieldUpdater | 1.5 | int | 它和AtomicInteger作用是一样的,但是AtomicInteger是直接作用在它本身,而AtomicIntegerFieldUpdater可以指定类变量 |
AtomicLong | 1.5 | long | 参考AtomicInteger |
AtomicLongArray | 1.5 | long[] | 参考AtomicIntegerArray |
AtomicLongFieldUpdater | 1.5 | long | 参考AtomicIntegerFieldUpdater |
AtomicReference | 1.5 | 任何引用类型 | 保证引用类型变量原子性更新等操作 |
AtomicReferenceArray | 1.5 | 任何引用类型数组 | 保证引用类型数组变量原子性更新等操作 |
AtomicReferenceFieldUpdater | 1.5 | 任何引用类型 | 参考AtomicIntegerArray |
AtomicStampedReference | 1.5 | 任何引用类型 | 功能和AtomicReference一样,但是通过时间戳标记更新,解决了CAS的ABA问题 |
AtomicMarkableReference | 1.5 | 任何引用类型 | 功能和AtomicReference一样,但是通过布尔类型标记是否更新,解决了CAS的ABA问题,它比AtomicStampedReference更轻一点 |
LongAdder | 1.8 | long | 和AtomicLong功能相似,但是解决了cas在高并发下循环自旋的问题,不过代价就是结果不实时 |
LongAccumulator | 1.8 | long | LongAdder是它的一种特例,它的功能更强大 |
DoubleAdder | 1.8 | double | 参考LongAdder |
LongAccumulator | 1.8 | double | 参考LongAccumulator |
if(v == a){
v = b;
}
我们知道按照上面代码的写法,一定会存在并发问题。但是CPU帮我提供了一种底层方式保证这种操作的原子性,这就是CAS(Compare and swap)。它通过对比某一个内存地址的值之后再更新,每次只能有一个线程操作成功,从而保证其原子性。
在介绍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();
}
}
如果你执行上面的代码会发现,极大可能v的最终结果不是100000,原因就是volatile不能保证原子性。
可是,通过前面cas介绍,我们知道它可以保证原子性,而volatile又能保证可见性和有序性,所以它们就’双剑合璧’了,产生了我们看到的atomic类。
说明:这里只是简单介绍CAS和volatile变量,给大家一个直观的感受,后面会详细介绍jvm内存模型,CAS存在的问题等内容。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。