Java多线程:CAS与java.util.concurrent

2019-04-01  本文已影响0人  CieloSun

乐观锁与悲观锁

悲观锁

总是假设最坏的情况,每次获取数据都认为别人会修改,所以拿数据时会上锁,一直到释放锁不允许其他线程修改数据。Java中如synchronized和reentrantLock就是这种实现。

乐观锁

总是假设最好的情况,每次去拿数据时都认为别人不会修改,所以不上锁,等更新数据时判断一下在此期间是否有其他人更新过这个数据,可以使用CAS算法实现。乐观锁适用于多读少写的应用类型,可以大幅度提高吞吐量。乐观锁的实现机制主要包括版本号机制(给数据加一个版本号,数据被修改版本号会加一,更新时读取版本号,若读取到的版本号和之前一致才更新,否则驳回)和CAS算法(下详)。

CAS算法

JDK5增加java.util.concurrent包,其下类通过CAS算法实现了乐观锁实现。

CAS即compare and swap,是一种有名的无锁算法,即不使用锁的情况下实现多线程之间的变量同步,也叫非阻塞同步。

CAS有三个操作数,内存值V,旧的预期内存值A,要修改的新值B,当且仅当A=V,才将内存值V修改为B,否则不会执行任何操作。一般情况下CAS是一个自旋操作,即不断重试。

CAS只能保证一个共享变量的原子操作,当操作涉及跨多个共享变量时CAS无效。但可用AtomicReference来保证引用对象之间的原子性。

CAS开销

CAS是CPU指令集的操作,只有一步的原子操作,所以非常快,但CAS的开销主要在于cache miss问题。如图

image

这是一个8核CPU系统,共有4个管芯,每个管芯中有两个CPU,每个CPU有cache,管芯内有一个互联模块,让管芯的两个核可以互相通信。图中的系统连接模块可以让四个管芯通信。例如,此时CPU0进行一个CAS操作,而该变量所在的缓存线在CPU7的高速缓存中,则流程如下:

此外,资源竞争时,CAS会自旋(不成功就循环到成功),

ABA问题

CAS一个常见问题是ABA问题,即变量V初次读时是A值,被赋值时也是A值,但期间变量被赋值成B值,CAS会误认为他从没被修改过。JDK1.5后的AtomicStampedReference类提供了监测ABA问题的能力,其中的compareAndSet方法首先检查当前引用是否等于预期引用,并且当前标志等于预期标志,全部相等则以原子方式将该引用和该标志的值设置为给定的更新值。

CAS与synchronized

java.util.concurrent.atomic

jdk1.5提供了一组原子类,由CAS对其实现。其中的类可以分为四组:

其作用为对单一数据的操作实现原子化,无需阻塞代码,但访问两个或两个以上的atomic变量或对单个atomic变量进行2次或2次以上的操作被认为是需要同步的以便这些操作是一个原子操作。

AtomicBoolean, AtomicInteger, AtomicLong, AtomicReference

这四种类型用来处理Boolean,Integer, Long, 对象,包含以下方法:

对于AtomicInteger, AtomicLong,还实现了getAndIncrement(),increateAndGet(),getAndDecreate(),decreateAndGet(),addAndGet(),getAndAdd()方法,以实现加减法的原子操作。剩下部分代码我还在阅读中,不定期补充。

参考文献

深入理解CAS算法原理
面试必备之乐观锁与悲观锁
Java之多线程 Atomic(原子的)
对 volatile、compareAndSet、weakCompareAndSet 的一些思考

上一篇 下一篇

猜你喜欢

热点阅读