锁是用来作并发最简单的方式,其代价也是最高的,Java在JDK1.5以前都是靠synchronized关键字来加锁。可是加锁机制会有以下几个问题:java
悲观锁:是认为别的线程会修改值。
独占锁是一种悲观锁,synchronized就是一种独占锁。synchronized加锁后就可以确保程序执行时不会被其它线程干扰,获得正确的结果。算法
乐观锁:本质上是乐观的,认为别的线程不会去修改值。若是发现值被修改了,能够再次重试。CAS机制(Compare And Swap)就是一种乐观锁。编程
CAS是一种有名的无锁(lock-free)算法。也是一种现代 CPU 普遍支持的CPU指令级的操做,只有一步原子操做,因此很是快。并且CAS避免了请求操做系统来裁定锁的问题,不用麻烦操做系统,直接在CPU内部就搞定了。缓存
CAS有三个操做参数:并发
CAS的操做过程:将内存位置V的值与A比较(compare),若是相等,则说明没有其它线程来修改过这个值,因此把内存V的的值更新成B(swap),若是不相等,说明V上的值被修改过了,不更新,而是返回当前V的值,再从新执行一次任务再继续这个过程。测试
因此,当多个线程尝试使用CAS同时更新同一个变量时,其中一个线程会成功更新变量的值,剩下的会失败。失败的线程能够重试或者什么也不作。this
简单来讲,CAS 的含义是“我认为原有的值应该是什么,若是是,则将原有的值更新为新值,不然不作修改,并告诉我这个值如今是多少”。(这段描述引自《Java并发编程实践》)atom
在JDK1.5以前,若是不编写明确的代码就没法执行CAS操做,在JDK1.5中引入了底层的支持,在int、long和对象的引用等类型上都公开了CAS的操做,而且JVM把它们编译为底层硬件提供的最有效的方法,在运行CAS的平台上,运行时把它们编译为相应的机器指令,若是处理器/CPU不支持CAS指令,那么JVM将使用自旋锁。操作系统
在原子类变量中,如java.util.concurrent.atomic包下的AtomicXXX,都使用了这些底层的JVM支持为数字类型的引用类型提供一种高效的CAS操做,而在java.util.concurrent中的大多数类在实现时都直接或间接的使用了这些原子变量类。线程
java.util.concurrent.atomic.AtomicLong源码中的自增getAndIncrement()方法:
//+1操做 public final long getAndIncrement() { while (true) { long current = get(); long next = current + 1; //当+1操做成功的时候直接返回,退出此循环 if (compareAndSet(current, next)) return current; } } //调用JNI实现CAS public final boolean compareAndSet(long expect, long update) { return unsafe.compareAndSwapLong(this, valueOffset, expect, update); }
JNI:Java Native Interface为JAVA本地调用,容许java调用其余语言。在jdk1.8后getAndIncrement()
方法已经看不到具体代码了,而是封装在unsafe类里面。
CAS虽然很高效的解决原子操做,可是CAS仍然存在三大问题。ABA问题,循环时间长开销大和只能保证一个共享变量的原子操做。
从Java1.5开始JDK的atomic包里提供了一个类AtomicStampedReference来解决ABA问题。这个类的compareAndSet方法做用是首先检查当前引用是否等于预期引用,而且当前标志是否等于预期标志,若是所有相等,则以原子方式将该引用和该标志的值设置为给定的更新值。
能够用CAS在无锁的状况下实现原子操做,但要明确应用场合,很是简单的操做且又不想引入锁能够考虑使用CAS操做,当想要非阻塞地完成某一操做也能够考虑CAS。不推荐在复杂操做中引入CAS,会使程序可读性变差,且难以测试,同时会出现ABA问题。