老王:小陈啊,从今天开始我们就要进入Atomic原子类系列的学习了,首先啊给你看一下JDK中提供给我们使用的原子类有哪些?
小陈:好啊,我记得JUC下面提供的原子类还是挺多的,所有的原子类的使用和底层实现的原理都全部要掌握吗?
老王:其实啊,JUC包下提供的原子类底层的实现原理基本都是差不多的,都是基于volatile和CAS操作来保证线程安全的,我们只需要掌握常用的一些原子类就可以了。
老王:至于这些原子类底层实现的原理,我们挑选几个比较有代表性的来讲,深入到源码级别,以及底层实现的细节;理解了我们讨论的这几个,你再去看其它的几个实现,发现都是一样的套路。
老王:哎呀,废话多了,不多说了,直接给你看下JUC给我哪些原子类:
看上面的图形,我们使用红色圈中的那些,就是我们需要讨论的,一共分为一下几个类别:
(1)AtomicInteger、AtomicLong整型的原子类
如果我们在程序中使用了基本的Integer、Long等类型,进行基本的数学加减运算、赋值操作等。但是如果是在多线程环境下多个线程同时操作同一个Integer、Long变量,可能导致线程安全的问题。
要规避线程安全的问题,你可能需要用到锁,比如常用的synchronized、ReentrantLock锁等,让同一时间只有一个线程操作变量;但是锁是一个比较重的操作,可能导致线程获取不到锁而陷入沉睡,这样就会导致并发的性能降低。
然而如果你需要很高的并发性能,不想用锁,但需要是线程安全的;在这种情况下JUC就为我们提供了线程安全的原子类AtomicInteger、AtomicLong等,在多线程环境下进行加减运算、赋值操作等都是线程安全的。
AtomicInteger、AtomicLong使用非常广泛,比如你的系统里面可能用到一些统计、计数场景,比如每秒访问的流量、注册中心每秒接收到注册的次数、心跳的次数、可以使用这两个原子类。在一些开源的分布式系统、中间件系统里面,大量的metric指标统计的功能都是基于AtomicInteger、AtomicLong这两个原子类去实现的,非常重要。
(2)AtomicBoolean 布尔类型的原子类
在多线程环境下我们使用基本的boolean、Boolean类型的布尔变量是非线程安全的,JUC提供类线程安全的布尔类型原子类AtomicBoolean。AtmoicBoolean 通常使用的场景是一些状态标识,比如初始化标识、系统关闭标识、逻辑状态判断标识、状态开关等等。
此外AtomicBoolean 还经常使用做非常轻量级的锁,比如阿里开源的分布式消息中间件RocketMQ就使用了AtomicBoolean封装了一个自旋锁,具有非常高的并发性能。(CAS将AtomicBoolean设置成true表示获取到了锁,释放锁的时候CAS操作将AtomicBoolean设置为false)
(3)AtomicReference、AtomicStampReference可以封装对象的原子类
上面的提供的AtomicInteger、AtomicLong、AtomicBoolean等都是基本类型对应的原子类;但是如果你共享的是一个对象呢?多个线程同时修改一个对象呢? 在不使用锁的场景下可以将对象使用AtomicReference进行包装,然后CAS的去修改对象,也可以达到线程安全的效果。
AtomicStampReference 则是AtomicReference的升级版本,提供版本号机制,解决CAS中经典的ABA问题
(5)LongAdder原子类
是AtomicLong原子类的一个升级版本,在并发竞争非常激烈的时候可能会导致大量的线程CAS失败而不断自旋,而LongAdder采用分段锁的思想,在并发竞争非常激烈的时候使得不同的线程去竞争不同的锁;这样就可以减少对同一个锁的竞争,优化了并发的性能。
小陈:哇塞,原子类还有这么多分类啊,厉害了...,老王啊,我们从哪里开始学,我等不及了,等我学完回去大展拳脚......
老王:小陈啊,我们本章的主题是概览,我们先大概了解一下Atomic的类体系、我们需要掌握哪些、分为哪些类等,具体的学习我们留到后面的讨论......
小陈:这样啊,那我先预习预习,明天等你来讲...
老王:好,本篇那就先到这里......
小陈:好的,我们下一章见。