java核心-多线程(8)- 并发原子类
使用锁能解决并发时线程安全性,但锁的代价比较大,而且降低性能。有些时候可以使用原子类(juc-atomic包中的原子类)。还有一些其他的非加锁式并发处理方式,我写这篇文章来源于Java中有哪些无锁技术来解决并发问题的思考。
1.原子类场景
刚才说了,原子类是在不加锁的情况下,实现并发安全。我们知道锁synchronized/lock能实现并发安全的三点要求:原子性、可见性和有序性。而原子类顾名思义可以保证原则性其他两点不能确定(我是边想边写的,后面我会给出结论)。
好吧,写不下去了,还是看看现成的总结吧。百度收索“Java原子类使用场景”,随便点开两个,基本上都是通过举了一个“多线程计数”的例子来说明使用场景+底层使用CAS来总结原理。好了这里我就得出结论原子类使用场景之一多线程计数器的实现。后面如果找到更好的总结再补充。
这里说一句废话,看似我在写一些没有用心总结的东西,也不会有人看,网上这样的文章一堆,我干嘛要写呢,我告诉你,我主要是形成自己关于这块的知识的痕迹,写的虽然不怎么的,但至少我也是按结构,经过思考过的输出产品。这一系列的文章最终能搭建我的知识体系,知识体系一旦搭建完成,就能建立知识地图,快速反应和检索,思考和工作效率得到提升。我深信这一点。
#####2.原子类分类 ``` 分类这块看到一个比较好的总结,[Java16个原子类介绍-基于JDK8](https://blog.csdn.net/weixin_38003389/article/details/88569336),讲的很全面,每个都有例子说明 总结一下: 1.jdk8之前共12个,jdk8新增了4个 2.具体分类 a.原子基本类型:AtomicInteger、AtomicBoolean、AtomicLong b.原子数组(通过原子操作的方式更新数组中元素):AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray c.原子引用类型:AtomicRerence、AtomicMarkableReference d.原子字段类(通过原子操作更新某个类的字段):AtomicIntegerFieldUpdater、AtomicLongFieldUpdater、AtomicStampedFieldUpdater、AtomicReferenceFieldUpdater e.jdk8新增:DoubleAccumulator、LongAccumulator、DoubleAdder、LongAdder
3.具体使用注意
a.要想使用原子字段类需要两步。第一步,因为原子更新字段类都是抽象类,每次使用的时候必须使用静态方法newUpdater()创建一个更新器,并且需要设置想要更新的类和属性。第二步,更新类的字段必须使用 public volatile 修饰。
<br>
#####3.原子类实现原理
a.大多数使用Unsafe类的CAS原子指令。
b.jdk8新增的LongAdder使用的不是Unsafe的CAS原理,而是类似的思想,下图很好的说明了这种思想。LongAdder不可以代替AtomicLong ,虽然 LongAdder 的 add() 方法可以原子性操作,但是并没有使用 Unsafe 的CAS算法,只是使用了CAS的思想。
![](https://img2018.cnblogs.com/blog/1012214/202001/1012214-20200122180604029-714306250.jpg)
如图LongAdder则是内部维护多个变量,每个变量初始化都0,在同等并发量的情况下,争夺单个变量的线程量会减少这是变相的减少了争夺共享资源的并发量,另外多个线程在争夺同一个原子变量时候如果失败并不是自旋CAS重试,而是尝试获取其他原子变量的锁,最后获取当前值时候是把所有变量的值累加后返回的(不一定准确,没有并发情况下真实)。
<br>
#####4.原子类使用demo
public class TestAtomicInteger {
static Integer count = 0;
public static void main(String[] args){
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 100000; i++) {
count++;
}
System.out.println("thread1 count add 100000 over");
}
});
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 100000; i++) {
count++;
}
System.out.println("thread2 count add 100000 over");
}
});
thread1.start();
thread2.start();
try {
thread1.join();
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("最终:"+count);
}
}
//该例子输出的结果并不是200000,而是小于它,只需要把count的类型改为AtomicInteger即可(前面不需要加volatile关键字)。