CAS缺陷
CAS虽然高效地解决了原子操作,但是还是存在一些缺陷的 ,主要表现在三个方面:循环时间太长、只能保证一个共享变量原子操作、ABA问题。
●循环时间太长
如果CAS一直不成功呢?这种情况绝对有可能发生。如果自旋CAS长时间地不成功,则会给CPU带来非常大的开销。在JUC中有些地方就限制了CAS自旋的次数,例如BlockingQueue的SynchronousQueue,
●只能保证一个共享变量原子操作
看了CAS的实现就知道这只能针对一个共享变量,如果是多个共享变量就只能使用锁了。
●ABA问题
CAS需要检查操作值有没有发生改变,如果没有发生改变则更新。但是存在这样一种情况 :如果一个值原来是A,变成了B,然后又变成了A.那么在CAS检查的时候会发现没有改变,但是实质上它已经发生了改变,这就是所谓的ABA问题。
CAS的ABA隐患问题的解决
对于ABA问题其解决方案是加上版本号。即在每个变量都加上一个版本号,每次改变时加1 ,即 A->B->A 变成 1A -> 2B -> 3A。:
Java提供了AtomicStampedReference(添加版本戳)来解决。AtomicstampedReference通过包装 [E, Integer] 的元组来对对象标记版本戳stamp ,从而避免ABA问题。对于上面的实例应该线程1会失败。
J.U.C之atomic包
atomic包介绍
通过前面CAS的学习,我们了解到AtomicInteger的工作原理:它们的内部都维护者一个对应的基本类型的成员变量value ,这个变量是被volatile关键字修饰的,保证多线程环境下看见的是同一个(可见性) 。 AtomicInteger在进行一些原子操作的时候 ,依赖Unsafe类里面的CAS方法,原子操作就是通过自旋方式,不断地使用CAS函数进行尝试直到达到自己的目的。
除了AtomicInteger类以外还有很多其他的类也有类似的功能,在JUC中有一个包java.util.concurrent.atomic存放原子操作的类, atomic里的类主要包括:
●基本类型
使用原子的方式更新基本类型
AtomicInteger :整形原子类
AtomicLong :长整型原子类
AtomicBoolean :布尔型原子类
AtomicInteger主要API如下:
get()//直接返回值
getAndAdd(int)//增加指定的数据,返回变化前的数据
getAndDecrement() //减少1 ,返回减少前的数据_
getAndIncrement() //增加1 ,返回增加前的数据
getAndSet(int)//设置指定的数据,返回设置前的数据
addAndGet(int)//增加指定的数据后返回增加后的数据
decrementAndGet() //减少1 ,返回减少后的值
incrementAndGet() //增加1 ,返回增加后的值
lazySet(int)//仅仅当get时才会set
compareAndSet(int, int)//尝试新增后对比 ,若增加成功则返回true否则返回false
AtomicLong主要API和AtomicInteger ,只是类型不是int ,而是long
●引用类型
AtomicReference :引用类型原子类
AtomicStampedRerence :原子更新引用类型里的字段原子类
AtomicMarkableReference : 原子更新带有标记位的引用类型
●数组类型
使用原子的方式更新数组里的某个元素
AtomicIntegerArray :整形数组原子类
AtomicLongArray ;长整形数组原子类
AtomicReferenceArray :引用类型数组原子类
AtomicIntegerArray主要API如下:
addAndGet(int, int)//执行加法,第一个参数为数组的下标, 第二个参数为增加的数量,返回增加后的结果
compareAndSet(int, int, int)// 对比修改,参1数组下标,参2原始值,参3修改目标值,成功返回true否则false
decrementAndGet(int)//参数为数组下标,将数组对应数字减少1,返回减少后的数据
incrementAndGet(int)//参数为数组下标,将数组对应数字增加1,返回增加后的数据
getAndAdd(int, int)// 和addAndGet类似,区别是返回值是变化前的数据
getAndDecrement(int)//和decrementAndGet类似 ,区别是返回变化前的数据
getAndIncrement(int)//和incrementAndGet类似 ,区别是返回变化前的数据
getAndSet(int, int)// 将对应下标的数字设置为指定值,第二个参数为设置的值,返回是变化前的数据
AtomicintegerArray主要API和AtomicLongArray ,只是类型不是int ,而是long
●对象的属性修改类型
AtomicIntegerFieldUpdater.原子更新整形字段的更新器
AtomicL ongFieldUpdater :原子更新长整形字段的更新器
AtomicStampedReference :原子更新带有版本号的引用类型。
●JDK1.8新增类
DoubleAdder :双浮点型原子类
LongAdder :长整型原子类
DoubleAccumulator :类似DoubleAdder ,但要更加灵活(要传入一个函数式接口)
LongAccumulator :类似LongAdder ,但要更加灵活(要传入-个函数式接口)
虽然涉及到的类很多,但是原理和AtomicInteger都是一样,使用CAS进行的原子操作,其方法和使用都是大同小异的。