前段时间有人问我java中的乐观锁和悲观锁的问题,我被问愣神了,乐观锁和悲观锁我到是据说过,在数据库里面应用极广,可是java里面就好像没有据说过,后来我详细去看了下《java编程思想》,的确在java里面有乐观锁的描述。java
悲观锁:悲观锁是认为确定有其余线程来争夺资源,所以无论到底会不会发生争夺,悲观锁老是会先去锁住资源。算法
乐观锁:所谓的乐观锁就是当去作某个修改或其余操做的时候它认为不会有其余线程来作一样的操做(竞争),这是一种乐观的态度,一般是基于CAS原子指令来实现的。关于CAS能够参见这篇文章java并发包的CAS操做,CAS一般不会将线程挂起,所以有时性能会好一些。数据库
CAS就是Compare and Swap的意思,比较并操做。不少的cpu直接支持CAS指令。CAS是项乐观锁技术,当多个线程尝试使用CAS同时更新同一个变量时,只有其中一个线程能更新变量的值,而其它线程都失败,失败的线程并不会被挂起,而是被告知此次竞争中失败,并能够再次尝试。CAS有3个操做数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改成B,不然什么都不作。编程
JDK1.5中引入了底层的支持,在int、long和对象的引用等类型上都公开了CAS的操做,而且JVM把它们编译为底层硬件提供的最有效的方法,在运行CAS的平台上,运行时把它们编译为相应的机器指令。在java.util.concurrent.atomic包下面的全部的原子变量类型中,好比AtomicInteger,都使用了这些底层的JVM支持为数字类型的引用类型提供一种高效的CAS操做。安全
在CAS操做中,会出现ABA问题。就是若是V的值先由A变成B,再由B变成A,那么仍然认为是发生了变化,并须要从新执行算法中的步骤。有简单的解决方案:不是更新某个引用的值,而是更新两个值,包括一个引用和一个版本号,即便这个值由A变为B,而后为变为A,版本号也是不一样的。AtomicStampedReference和AtomicMarkableReference支持在两个变量上执行原子的条件更新。AtomicStampedReference更新一个“对象-引用”二元组,经过在引用上加上“版本号”,从而避免ABA问题,AtomicMarkableReference将更新一个“对象引用-布尔值”的二元组。并发
在AtomicInteger的里面的实现乐观锁的机制。性能
AtomicInteger 是一个支持原子操做的 Integer 类,就是保证对AtomicInteger类型变量的增长和减小操做是原子性的,不会出现多个线程下的数据不一致问题。若是不使用 AtomicInteger,要实现一个按顺序获取的 ID,就必须在每次获取时进行加锁操做,以免出现并发时获取到一样的 ID 的现象。this
接下来经过源代码来看AtomicInteger具体是如何实现的原子操做。atom
首先看incrementAndGet() 方法,下面是具体的代码。spa
public final int incrementAndGet() { for (;;) { int current = get(); int next = current + 1; if (compareAndSet(current, next)) return next; } }
经过源码,能够知道,这个方法的作法为先获取到当前的 value 属性值,而后将 value 加 1,赋值给一个局部的 next 变量,然而,这两步都是非线程安全的,可是内部有一个死循环,不断去作compareAndSet操做,直到成功为止,也就是修改的根本在compareAndSet方法里面,compareAndSet()方法的代码以下:
public final boolean compareAndSet(int expect, int update) { return unsafe.compareAndSwapInt(this, valueOffset, expect, update); }
compareAndSet()方法调用的compareAndSwapInt()方法的声明以下,是一个native方法。
public final native boolean compareAndSwapInt(Object var1, long var2, int var4, intvar5);
compareAndSet 传入的为执行方法时获取到的 value 属性值,next 为加 1 后的值, compareAndSet所作的为调用 Sun 的 UnSafe 的 compareAndSwapInt 方法来完成,此方法为 native 方法,compareAndSwapInt 基于的是CPU 的 CAS指令来实现的。因此基于 CAS 的操做可认为是无阻塞的,一个线程的失败或挂起不会引发其它线程也失败或挂起。而且因为 CAS 操做是 CPU 原语,因此性能比较好。
相似的,还有decrementAndGet()方法。它和incrementAndGet()的区别是将 value 减 1,赋值给next 变量。
AtomicInteger中还有getAndIncrement() 和getAndDecrement() 方法,他们的实现原理和上面的两个方法彻底相同,区别是返回值不一样,前两个方法返回的是改变以后的值,即next。而这两个方法返回的是改变以前的值,即current。还有不少的其余方法,就不列举了。
悲观锁就再也不说了,基本你用synchronized和lock基本都是悲观锁。咱们只探讨以前没有研究过的。