原子变量
什么是原子变量
- 原子变量与Volatile变量很相似,但使用它们的代价比使用Volatile变量的代价更高;它们用于在不使用synchronized语句的情况下,执行复杂的并发操作。
- 原子变量有布尔型,整型,长整型,和引用类型
AtomicBoolean、AtomicInteger、AtomicLong、AtomicReference
什么是原子操作
- 原子变量被存储在支持复杂的可线性化操作的内存区域中。可线性化操作是指可立刻执行的任何操作,例如Volatile变量的写入操作就是一种可线性化操作。一个复杂的线性化操作至少包含2个步骤:读取和写入; 因此我们使用术语“原子操作”,代表这种复杂的可线性化操作。
使用原子操作执行getUniqueId方法(即UID的唯一)
object two_原子操作_getUniqueId extends App {
private val uid = new AtomicLong(0L) //定义了一个Long类型的原子变量
def getUniqueId: Long = uid.incrementAndGet() //使用了该变量的原子操作:incrementAndGet;它会读取uid然后计算+1的值,之后将结果写会变量uid中,并返回这个结果。
execute(println(s"Uid asynchronously : $getUniqueId")) // 创建个全局对象线程池
println(s"Got a unique id : $getUniqueId") //main线程也执行一次getUniqueId方法
Thread.sleep(1000)
}
重写_incrementAndGet并实现getUid
compareAndSet使用了源码中的compareAndSwapLong(valueOffset,oldValue,newValue) valueOffset表示的是原子变量uid的值在内存中如果发生了变化,发生变化后新值所在的地址,如果oldValue的值 == valueoffSet的值,那么就将newValue赋值给ValueOffset然后地址发生偏移,此时uid的值就是newValue,返回的是一个true如果oldValue != valueOffset那么返回的就是一个false
object 重写_incrementAndGet extends App {
private val uid: AtomicLong = new AtomicLong(0L) //定义了一个Long类型的原子变量
@tailrec def 重写_incrementAndGet: Long = {
val oldUid = uid.get()
val newUid = oldUid + 1
if (uid.compareAndSet(oldUid, newUid)) newUid else 重写_incrementAndGet
}
for (i <- 1 to 12) execute(println(s"ThreadName = ${Thread.currentThread().getName};Uid asynchronously : $重写_incrementAndGet")) // 创建个全局对象线程池
println(s"Got a unique id : $重写_incrementAndGet") //main线程也执行一次getUniqueId方法
Thread.sleep(1000)
}
什么是CompareAndSet(CAS)?
- 原子变量还定义了其他复杂的原子操作,例如getAndSet,decrementAndGet,addAndGet之类的方法。
- 而这些原子操作都实现了基本的原子操作(compareAndSet比较并转换操作),CAS会接收原子变量的当前值和新值,并在当前值等于预期值的情况下以原子方式将当前值替换为新值;
为什么无锁算法能够提高吞吐量?
因为执行无锁操作的线程不会尝试获取任何锁;
- 使用原子变量实现的操作基本都是无锁的,但这并不意味着使用原子变量的程序就一定是无锁的。
- 无锁操作的定义是:当一组线程执行一个操作时,如果不论这些线程以怎样的速度运行,它们之中至少有2个线程总是会在限定的时间内完成该操作,那么该操作就是一个无锁操作。(例如前面的重写_incrementAndGet操作方法,线程池中的线程至少有2个都会完成操作的,因为如果失败了还会调用该操作方法,而不会一直while,进入忙等待)
无锁编程?
- 在程序中使用锁,可能会引发死锁的情况;
- 但我们可以通过使用原子变量来实现无锁操作,如前面的”重写_incrementAndGet”就是一个通过原子变量来实现的getUid的无锁操作;
(本文章虽然采用的代码为scala代码,但java代码与Scala代码可以互相转换,且本质上两者所阐述的东西都是一致的)