学了这么久的高并发编程,连Java中的并发原子类都不知道?这也太Low了吧
Posted 冰 河
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了学了这么久的高并发编程,连Java中的并发原子类都不知道?这也太Low了吧相关的知识,希望对你有一定的参考价值。
大家好,我是冰河~~
这几天宅在家里看完了《分布式系统与一致性》这本书,挺不错的,后面给大家写几篇总结。
之前在票圈说写一篇,后面整理完脑图后发现一个问题,一篇文章根本说不清楚啊。除了要写一些书中的精华之外,更多的是要结合冰河实际工作中接触的分布式系统的真实架构案例,写一些冰河个人的总结和体会,希望能够为大家带来一些实质性的帮助。
好了,今天我们再上一篇高并发干货,一起来聊聊Java中的并发原子类。接下来,我们开始今天的正文吧。
本文结构
Java原子类
在 java.util.concurrent.atomic
包下有很多支持并发的原子类,某种程度上,我们可以将其分成:基本数据类型的原子类、对象引用类型的原子类、数组类型的原子类、对象属性类型的原子类和累加器类型的原子类 五大类。
接下来,我们就一起来看看这些并发原子类吧。
基本数据类型的原子类
基本数据类型的原子类包含:AtomicBoolean、AtomicInteger和AtomicLong。
打开这些原子类的源码,我们可以发现,这些原子类在使用上还是非常简单的,主要提供了如下这些比较常用的方法。
- 原子化加1或减1操作
//原子化的i++
getAndIncrement()
//原子化的i--
getAndDecrement()
//原子化的++i
incrementAndGet()
//原子化的--i
decrementAndGet()
- 原子化增加指定的值
//当前值+=delta,返回+=前的值
getAndAdd(delta)
//当前值+=delta,返回+=后的值
addAndGet(delta)
- CAS操作
//CAS操作,返回原子化操作的结果是否成功
compareAndSet(expect, update)
- 接收函数计算结果
//结果数据可通过传入func函数来计算
getAndUpdate(func)
updateAndGet(func)
getAndAccumulate(x,func)
accumulateAndGet(x,func)
对象引用类型的原子类
对象引用类型的原子类包含:AtomicReference、AtomicStampedReference和AtomicMarkableReference。
利用这些对象引用类型的原子类,可以实现对象引用更新的原子化。AtomicReference提供的原子化更新操作与基本数据类型的原子类提供的更新操作差不多,只不过AtomicReference提供的原子化操作常用于更新对象信息。这里不再赘述。
需要特别注意的是:使用对象引用类型的原子类,要重点关注ABA问题。
关于ABA问题,文章的最后部分会说明。
好在AtomicStampedReference和AtomicMarkableReference这两个原子类解决了ABA问题。
AtomicStampedReference类中的compareAndSet的方法签名如下所示。
boolean compareAndSet(V expectedReference, V newReference, int expectedStamp, int newStamp)
可以看到,AtomicStampedReference类解决ABA问题的方案与乐观锁的机制比较相似,实现的CAS方法增加了版本号。只有expectedReference的值与内存中的引用值相等,并且expectedStamp版本号与内存中的版本号相同时,才会将内存中的引用值更新为newReference,同时将内存中的版本号更新为newStamp。
AtomicMarkableReference类中的compareAndSet的方法签名如下所示。
boolean compareAndSet(V expectedReference, V newReference, boolean expectedMark, boolean newMark)
可以看到,AtomicMarkableReference解决ABA问题的方案就更简单了,在compareAndSet方法中,新增了boolean类型的校验值。这些理解起来也比较简单,这里,我也不再赘述了。
对象属性类型的原子类
对象属性类型的原子类包含:AtomicIntegerFieldUpdater、AtomicLongFieldUpdater和AtomicReferenceFieldUpdater。
利用对象属性类型的原子类可以原子化的更新对象的属性。值得一提的是,这三个类的对象都是通过反射的方式生成的,如下是三个类的newUpdater()方法。
//AtomicIntegerFieldUpdater的newUpdater方法
public static <U> AtomicIntegerFieldUpdater<U> newUpdater(Class<U> tclass, String fieldName)
//AtomicLongFieldUpdater的newUpdater方法
public static <U> AtomicLongFieldUpdater<U> newUpdater(Class<U> tclass, String fieldName)
//AtomicReferenceFieldUpdater的newUpdater方法
public static <U,W> AtomicReferenceFieldUpdater<U,W> newUpdater(Class<U> tclass,
Class<W> vclass,
String fieldName)
这里,我们不难看出,在AtomicIntegerFieldUpdater、AtomicLongFieldUpdater和AtomicReferenceFieldUpdater三个类的newUpdater()方法中,只有传递的Class信息,并没有传递对象的引用信息。如果要更新对象的属性,则一定要使用对象的引用,那对象的引用是在哪里传递的呢?
其实,对象的引用是在真正调用原子操作的方法时传入的。这里,我们就以compareAndSet()方法为例,如下所示。
//AtomicIntegerFieldUpdater的compareAndSet()方法
compareAndSet(T obj, int expect, int update)
//AtomicLongFieldUpdater的compareAndSet()方法
compareAndSet(T obj, long expect, long update)
//AtomicReferenceFieldUpdater的compareAndSet()方法
compareAndSet(T obj, V expect, V update)
可以看到,原子化的操作方法仅仅是多了一个对象的引用,使用起来也非常简单,这里,我就不再赘述了。
另外,需要注意的是:使用AtomicIntegerFieldUpdater、AtomicLongFieldUpdater和AtomicReferenceFieldUpdater更新对象的属性时,对象属性必须是volatile类型的,只有这样才能保证可见性;如果对象属性不是volatile类型的,newUpdater()方法会抛出IllegalArgumentException这个运行时异常。
数组类型的原子类
数组类型的原子类包含:AtomicIntegerArray、AtomicLongArray和AtomicReferenceArray。
利用数组类型的原子类可以原子化的更新数组里面的每一个元素,使用起来也非常简单,数组类型的原子类提供的原子化方法仅仅是在基本数据类型的原子类和对象引用类型的原子类提供的原子化方法的基础上增加了一个数组的索引参数。
例如,我们以compareAndSet()方法为例,如下所示。
//AtomicIntegerArray的compareAndSet()方法
compareAndSet(int i, int expect, int update)
//AtomicLongArray的compareAndSet()方法
compareAndSet(int i, long expect, long update)
//AtomicReferenceArray的compareAndSet()方法
compareAndSet(int i, E expect, E update)
可以看到,原子化的操作方法仅仅是对多了一个数组的下标,使用起来也非常简单,这里,我就不再赘述了。
累加器类型的原子类
累加器类型的原子类包含:DoubleAccumulator、DoubleAdder、LongAccumulator和LongAdder。
累加器类型的原子类就比较简单了:仅仅支持值的累加操作,不支持compareAndSet()方法。对于值的累加操作,比基本数据类型的原子类速度更快,性能更好。
使用原子类实现count+1
在并发编程领域,一个经典的问题就是count+1问题。也就是在高并发环境下,如何保证count+1的正确性。一种方案就是在临界区加锁来保护共享变量count,但是这种方式太消耗性能了。
如果使用Java提供的原子类来解决高并发环境下count+的问题,则性能会大幅度提升。
简单的示例代码如下所示。
public class IncrementCountTest
private AtomicLong count = new AtomicLong(0);
public void incrementCountByNumber(int number)
for(int i = 0; i < number; i++)
count.getAndIncrement();
可以看到,原子类实现count+1问题,既没有使用synchronized锁,也没有使用Lock锁。
从本质上讲,它使用的是无锁或者是乐观锁方案解决的count+问题,说的具体一点就是CAS操作。
CAS原理
CAS操作包括三个操作数:需要读写的内存位置(V)、预期原值(A)、新值(B)。如果内存位置与预期原值的A相匹配,那么将内存位置的值更新为新值B。
如果内存位置与预期原值的值不匹配,那么处理器不会做任何操作。
无论哪种情况,它都会在 CAS 指令之前返回该位置的值。(在 CAS 的一些特殊情况下将仅返回 CAS 是否成功,而不提取当前值。)
简单点理解就是:位置 V 应该包含值 A;如果包含该值,则将 B 放到这个位置;否则,不要更改该位置,只返回位置V现在的值。这其实和乐观锁的冲突检测+数据更新的原理是一样的。
ABA问题
因为CAS需要在操作值的时候检查下值有没有发生变化,如果没有发生变化则更新,但是如果一个值原来是A,变成了B,又变成了A,那么使用CAS进行检查时会发现它的值没有发生变化,但是实际上却变化了。
ABA问题的解决思路就是使用版本号。在变量前面追加上版本号,每次变量更新的时候把版本号加1,那么A-B-A 就会变成1A-2B-3A。
从Java1.5开始JDK的atomic包里提供的AtomicStampedReference类和AtomicMarkableReference类能够解决CAS的ABA问题。
关于AtomicStampedReference类和AtomicMarkableReference类前文有描述,这里不再赘述。
好了,今天就到这儿吧,我是冰河,我们下期见~~
写在最后
如果你想进大厂,想升职加薪,或者对自己现有的工作比较迷茫,都可以私信我交流,希望我的一些经历能够帮助到大家~~
推荐阅读:
- 《实践出真知:全网最强秒杀系统架构解密,不是所有的秒杀都是秒杀!!》
- 《从零到上亿用户,我是如何一步步优化MySQL数据库的?(建议收藏)》
- 《我用多线程进一步优化了亿级流量电商业务下的海量数据校对系统,性能再次提升了200%!!(全程干货,建议收藏)》
- 《我用多线程优化了亿级流量电商业务下的海量数据校对系统,性能直接提升了200%!!(全程干货,建议收藏)》
- 《我用10张图总结出了这份并发编程最佳学习路线!!(建议收藏)》
- 《高并发场景下一种比读写锁更快的锁,看完我彻底折服了!!(建议收藏)》
- 《全网最全性能优化总结!!(冰河吐血整理,建议收藏)》
- 《三天撸完了MyBatis,各位随便问!!(冰河吐血整理,建议收藏)》
- 《奉劝那些刚参加工作的学弟学妹们:要想进大厂,这些并发编程知识是你必须要掌握的!完整学习路线!!(建议收藏)》
- 《奉劝那些刚参加工作的学弟学妹们:要想进大厂,这些核心技能是你必须要掌握的!完整学习路线!!(建议收藏)》
- 《奉劝那些刚参加工作的学弟学妹们:这些计算机与操作系统基础知识越早知道越好!万字长文太顶了!!(建议收藏)》
- 《我用三天时间开发了一款老少皆宜的国民级游戏,支持播放音乐,现开放完整源代码和注释(建议收藏)!!》
- 《我是全网最硬核的高并发编程作者,CSDN最值得关注的博主,大家同意吗?(建议收藏)》
- 《毕业五年,从月薪3000到年薪百万,我掌握了哪些核心技能?(建议收藏)》
- 《我入侵了隔壁妹子的Wifi,发现。。。(全程实战干货,建议收藏)》
- 《千万不要轻易尝试“熊猫烧香”,这不,我后悔了!》
- 《清明节偷偷训练“熊猫烧香”,结果我的电脑为熊猫“献身了”!》
- 《7.3万字肝爆Java8新特性,我不信你能看完!(建议收藏)》
- 《在业务高峰期拔掉服务器电源是一种怎样的体验?》
- 《全网最全Linux命令总结!!(史上最全,建议收藏)》
- 《用Python写了个工具,完美破解了MySQL!!(建议收藏)》
- 《SimpleDateFormat类到底为啥不是线程安全的?(附六种解决方案,建议收藏)》
- 《MySQL 8中新增的这三大索引,直接让MySQL起飞了,你竟然还不知道!!(建议收藏)》
- 《撸完Spring源码,我开源了这个分布式缓存框架!!(建议收藏)》
- 《亿级流量高并发秒杀系统商品“超卖”了,只因使用的JDK同步容器中存在这两个巨大的坑!!(踩坑实录,建议收藏)》
- 《奉劝那些刚参加工作的学弟学妹们:要想学好并发编程,这些并发容器的坑是你必须要注意的!!(建议收藏)》
- 《公司的报表工具太难用,我三天撸了个Excel工具,运营小姐姐直呼太好用了,现已开源!!(建议收藏)》
- 《奉劝那些刚参加工作的学弟学妹们:要想进大厂,这些并发编程核心技能是你必须要掌握的!!(建议收藏)》
- 《阿里面试官:高并发大流量秒杀系统如何正确的解决库存超卖问题?(建议收藏)》
- 《Redis五大数据类型与使用场景汇总!!(含完整实战案例,建议收藏)》
好了,今天就到这儿吧,小伙伴们点赞、收藏、评论,一键三连走起呀,我是冰河,我们下期见~~
以上是关于学了这么久的高并发编程,连Java中的并发原子类都不知道?这也太Low了吧的主要内容,如果未能解决你的问题,请参考以下文章