文章目录
- Atomic*特点
- Atomic*系列
- Atomic*缺点
- 参考
Atomic*特点
- Atomic* 按锁的类型来区分应该是属于乐观锁,就是多个线程在读取使用变量的时候是互不影响,非阻塞式的。
- sun.misc.Unsafe里面包含了大量的C代码和多种直接操作内存和原子能力的方法,所以被标记为不安全的,Atomic*系列主要就是使用了这个类的能力,来实现了非阻塞时的线程安全。Unsafe是一个非对外的类,内部是采用了单例模式来实现的,如果需要使用的话,可以通过反射的方式获取Unsafe内部的theUnsafe成员变量,从而获取Unsafe的实例。
boolena success = U.compareAndSwap*(obj, argAddrOffset, expectValue, updateValue)
- Unsafe类中包含了大量compareAndSwap* 的方法,支持int、long、object、array等类型,参数都是类似的。
- obj指的是要修改的变量所在的类地址,也就是起始内存地址,数组则对应第一个元素的内存地址;
- argAddrOffset指的是基于obj的地址,需要偏移的地址,即待修改的变量地址=obj的起始地址+argAddrOffset,数组则直接对应下表,UnSafe这个类中有大量的方法可以获取某个变量的偏移量地址,如成员变量使用objectFieldOffset方法获取变量偏移量,静态变量使用staticFieldOffset方法获取变量便宜量;
- expectValue指的是期望的值,即上一次读取到该线程工作内存中的值,如果两个值一致,则说明变量没有被修改,即可以直接将updateValue的值更新到主内存,否则更新失败,需要注意的是,这个比较和更新的动作是原子的,都JVM底层定义的原子操作来保证其原子性;
- updateValue指的是待更新到主内存中的值。
- 返回值为true则说明更新成功,返回false则说明更新识别,有可能是其他的线程更细了这个值
U.getAnd*(obj,argAddrOffset,value)
- 获取并做某些处理,可以是增加也可以是替换,如getAndAddInt()、getAndSetInt()等,返回的值都是修改之前的值,并且这些操作都是源自的操作,有JVM底层来保证。
- UnSafe类只提供了先获取再更新的方法,所以Atomic中提供的更新再获取的方法都是上层基于getAnd方法自定义逻辑来做的。
- Atomic*通过UnSafe来保证原子性,通过使用volatile来修饰value变量从而保证可见性,从而达到了我们的目的。
Atomic*系列
- AtomicInteger
- AtomicBoolean【底层是通过AtomicInteger来实现的,通过0-false,1-true来表示】
- AtomicLong
- AtomicReference
都是用原子的方式来更新指定类型的值
- AtomicIntegerArray
- AtomicLongArray
- AtomicReferenceArray
可以直接操作数组的某个元素的值,保证更改元素的原子性,另外所有的array都是经过一次clone后才存储到Atomic*中的
- AtomicIntegerFieldUpdater
- AtomicLongFieldUpdater
- AtomicReferenceFieldUpdater
可以修改某些类的成员变量,前提是这个类的变量是非静态的,且是非final的,可访问的
- AtomicMarkableReference
- AtomicStampedReference
添加了版本号的原子类,可以用来解决ABA问题,Markable只支持true、false两种状态,stamped支持多种状态
- DoubleAccumulator
- DoubleAdder
- LongAccumulator
- LongAdder
jdk1.8增加的新特性,主要是增加了吞吐量,但不能替代Atomic*系列
Atomic*缺点
- 当线程之间存在激烈的竞争时,容易出现某个线程长时间无法插入数据,从而导致多次循环,浪费CPU资源的情况。
- 同时采用普通的CAS来保证数据的安全,还容易出现ABA的问题,即某个数据从一个初始状态,经过多个状态变化后又回到初始状态,却被当做该数据是没有被更改过,这种情况在某些业务场景中是有问题的,这种问题可以通过采用带版本号的修改的方式来解决,即给每一次修改都添加一个版本。
参考
Java JUC之Atomic系列12大类实例讲解和原理分解
线程安全性详解(原子性、可见性、有序性)