老是假设最坏的状况,每次获取数据都认为别人会修改,因此拿数据时会上锁,一直到释放锁不容许其余线程修改数据。Java中如synchronized和reentrantLock就是这种实现。html
老是假设最好的状况,每次去拿数据时都认为别人不会修改,因此不上锁,等更新数据时判断一下在此期间是否有其余人更新过这个数据,可使用CAS算法实现。乐观锁适用于多读少写的应用类型,能够大幅度提升吞吐量。乐观锁的实现机制主要包括版本号机制(给数据加一个版本号,数据被修改版本号会加一,更新时读取版本号,若读取到的版本号和以前一致才更新,不然驳回)和CAS算法(下详)。java
多线程互斥访问时会进入锁机制。互斥设计时会面临一个状况:没有得到锁的进程如何处理。一般有两种办法:一种是没有得到锁就阻塞本身,请求OS调度另外一个线程上的处理器,即互斥锁;另外一种时没有得到锁的调用者就一直循环,直到锁的持有者释放锁,即自旋锁。面试
自旋锁是一种较低级的保护数据的方式,存在两个问题:递归死锁,即递归调用时试图得到相同的自旋锁。过多占用CPU资源,自旋锁不成功时会持续尝试,一般一个自旋锁会有参数限制尝试次数,超出后放弃time slice,等待一下一轮机会。算法
但在锁持有者保持锁的时间较短的前提下,选择自旋而非睡眠则大大提升了效率,于是在这种状况下自旋锁效率远高于互斥锁。编程
CAS即compare and swap,是一种系统原语,是不可分割的操做系统指令。CAS是一种乐观锁实现。数组
CAS有三个操做数,内存值V,旧的预期内存值A,要修改的新值B,当且仅当A=V,才将内存值V修改成B,不然不会执行任何操做。通常状况下CAS是一个自旋操做,即不断重试。缓存
CAS是CPU指令集的操做,只有一步的原子操做,因此很是快,CAS的开销主要在于cache miss问题。如图多线程
这是一个8核CPU系统,共有4个管芯,每一个管芯中有两个CPU,每一个CPU有cache,管芯内有一个互联模块,让管芯的两个核能够互相通讯。图中的系统链接模块可让四个管芯通讯。例如,此时CPU0进行一个CAS操做,而该变量所在的缓存线在CPU7的高速缓存中,则流程以下:并发
JDK5增长java.util.concurrent包,其中不少类使用了CAS操做。这些CAS操做基于Unsafe类中的native方法实现:app
//第一个参数o为给定对象,offset为对象内存的偏移量,经过这个偏移量迅速定位字段并设置或获取该字段的值, //expected表示指望值,x表示要设置的值,下面3个方法都经过CAS原子指令执行操做, //设置成功返回true,不然返回false。 public final native boolean compareAndSwapObject(Object o, long offset,Object expected, Object x); public final native boolean compareAndSwapInt(Object o, long offset,int expected,int x); public final native boolean compareAndSwapLong(Object o, long offset,long expected,long x);
因为CAS做用的对象在主存里而不是在线程的高速缓存里,CAS操做在Java中须要配合volatile使用。
Java中的CAS主要包含如下几个问题:
jdk1.5提供了一组原子类,由CAS对其实现。其中的类能够分为四组:
其做用为对单一数据的操做实现原子化,无需阻塞代码,但访问两个或两个以上的atomic变量或对单个atomic变量进行2次或2次以上的操做被认为是须要同步的以便这些操做是一个原子操做。
前四种类型用来处理Boolean,Integer, Long, 对象,后两个类支持的方法和AtomicReference基本一致,仅做用不一样。以上类型均包含如下方法:
对于AtomicInteger, AtomicLong,还实现了getAndIncrement(), increateAndGet(), getAndDecreate(), decreateAndGet(), addAndGet(delta), getAndAdd(delta)方法,以实现加减法的原子操做。
这三种类型用于处理数组,经常使用方法以下:
对于AtomicIntegerArray, AtomicLongArray,还实现了getAndIncrement(index), increateAndGet(index), getAndDecreate(index), decreateAndGet(index), addAndGet(index, delta), getAndAdd(index, delta)方法,以实现加减法的原子操做。
这三种类型用于处理普通对象中某个字段的CAS更新,因为是CAS更新,要求该字段必须是volatile的,经常使用方法以下:
示例操做以下:
User类(由普通类改形成的CAS更新类)
public class User { private static AtomicReferenceFieldUpdater<User, String> nameUpdater = AtomicReferenceFieldUpdater.newUpdater(User.class, String.class, "name"); private static AtomicIntegerFieldUpdater<User> ageUpdater = AtomicIntegerFieldUpdater.newUpdater(User.class, "age"); private volatile String name; private volatile int age; public User(String name, Integer age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public void lazySetName(String name) { nameUpdater.lazySet(this, name); } public String getSetName(String name) { return nameUpdater.getAndSet(this, name); } public void compareAndSetName(String exceptedName, String newName) { nameUpdater.compareAndSet(this, exceptedName, newName); } public void lazySetAge(int age) { ageUpdater.lazySet(this, age); } public Integer getSetAge(int age) { return ageUpdater.getAndSet(this, age); } public void compareAndSetAge(int exceptedAge, int newAge) { ageUpdater.compareAndSet(this, exceptedAge, newAge); } }
主程序
public class AtomicTest { public void run() { User user = new User("Atomic", 10); user.compareAndSetName("Atomic", "Ass"); user.compareAndSetAge(10, 11); System.out.println(user.getName() + user.getAge()); } public static void main(String[] args) throws Exception { new AtomicTest().run(); } }
输出结果:
Ass11
深刻理解CAS算法原理
面试必备之乐观锁与悲观锁
Java之多线程 Atomic(原子的)
对 volatile、compareAndSet、weakCompareAndSet 的一些思考
并发编程面试必备:JUC 中的 Atomic 原子类总结
AtomicReference,AtomicStampedReference与AtomicMarkableReference的区别
JAVA中的CAS