前段時間有人問我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基本都是悲觀鎖。咱們只探討以前沒有研究過的。