1. 悲觀鎖與樂觀鎖
咱們都知道,cpu是時分複用的,也就是把cpu的時間片,分配給不一樣的thread/process輪流執行,時間片與時間片之間,須要進行cpu切換,也就是會發生進程的切換。切換涉及到清空寄存器,緩存數據。而後從新加載新的thread所需數據。當一個線程被掛起時,加入到阻塞隊列,在必定的時間或條件下,在經過notify(),notifyAll()喚醒回來。在某個資源不可用的時候,就將cpu讓出,把當前等待線程切換爲阻塞狀態。等到資源(好比一個共享數據)可用了,那麼就將線程喚醒,讓他進入runnable狀態等待cpu調度。這就是典型的悲觀鎖的實現。獨佔鎖是一種悲觀鎖,synchronized就是一種獨佔鎖,它假設最壞的狀況,而且只有在確保其它線程不會形成干擾的狀況下執行,會致使其它全部須要鎖的線程掛起,等待持有鎖的線程釋放鎖。
可是,因爲在進程掛起和恢復執行過程當中存在着很大的開銷。當一個線程正在等待鎖時,它不能作任何事,因此悲觀鎖有很大的缺點。舉個例子,若是一個線程須要某個資源,可是這個資源的佔用時間很短,當線程第一次搶佔這個資源時,可能這個資源被佔用,若是此時掛起這個線程,可能馬上就發現資源可用,而後又須要花費很長的時間從新搶佔鎖,時間代價就會很是的高。
因此就有了樂觀鎖的概念,他的核心思路就是,每次不加鎖而是假設沒有衝突而去完成某項操做,若是由於衝突失敗就重試,直到成功爲止。在上面的例子中,某個線程能夠不讓出cpu,而是一直while循環,若是失敗就重試,直到成功爲止。因此,當數據爭用不嚴重時,樂觀鎖效果更好。好比CAS就是一種樂觀鎖思想的應用。
2. java中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將更新一個「對象引用-布爾值」的二元組。
3. AtomicInteger的實現。
AtomicInteger 是一個支持原子操做的 Integer 類,就是保證對AtomicInteger類型變量的增長和減小操做是原子性的,不會出現多個線程下的數據不一致問題。若是不使用 AtomicInteger,要實現一個按順序獲取的 ID,就必須在每次獲取時進行加鎖操做,以免出現併發時獲取到一樣的 ID 的現象。
接下來經過源代碼來看AtomicInteger具體是如何實現的原子操做。
首先看incrementAndGet() 方法,下面是具體的代碼。
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方法。
publicfinal 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。還有不少的其餘方法,就不列舉了。