1. 程式人生 > >Java 多執行緒高併發 2 — CAS 無鎖

Java 多執行緒高併發 2 — CAS 無鎖

在 Java 併發程式設計裡面,最可愛的就是無鎖了,非常巧妙,精彩絕倫 額。O__O "…

那麼什麼是無鎖?

  • 顧名思義,在併發情況下采用無鎖的方式實現物件操作的原子性,保證資料一致性、安全性、正確性

怎麼做?

  • 採用 CAS (Compare And Swap)的方式操作
  • 核心思想是:比對舊資料,如果沒人動過或者舊資料和預想的一樣,就執行更新等操作
  • 實現的原理是 CAS(V, E, N) ,當且緊當 E = V 時,N 才更新到 V 中,這裡的 V 是變數名,E 是期望值,N 是要更新到 V 的值
  • java.util.concurrent.atomic 包下有好幾種具體的實現,物件記憶體空間地址以及偏移量計算比較多,CRUD 慣了,讀起來可能有點吃力哈~

那麼問題來了

  • 同樣需要比對啊,同樣有 取值 — 比較 — 更新 的操作啊,多執行緒情況下,佢完值後別的執行緒同樣可以更新啊,那怎麼辦?

不用怕

  • 這裡的 取值 — 比較 — 更新 的操作是更加底層的系統裡面的一條 CPU 指令來完成,主要用到 sun.misc.Unsafe 提供的作業系統硬體級別的原子操作,並不是原來 Java 程式碼層面上寫好幾行的操作(好幾行程式碼已經有很多條 CPU 指令了),網路上很多高併發元件都有用到這個

下面是在 AtomicReferenceArray 之上封裝了一個簡易無鎖版本的 Stack

package jvm.concurrent.atomic;
import java.util.concurrent.atomic.AtomicReferenceArray; public class AtomicStack<E> { // 用 AtomicReferenceArray 來存放資料 protected AtomicReferenceArray<E> elementData; // 用於 棧的擴容,相當於臨時變數 protected AtomicReferenceArray<E> elementDataCopy; // 棧中資料數量 protected int
elementCount = 0; // 每次擴容的數量 protected int capacityIncrement = 10; public AtomicStack() { elementData = new AtomicReferenceArray<E>(capacityIncrement); } public E push(E item) { // 先檢查容量,不夠見擴容 ensureCapacity(); elementData.set(elementCount, item); elementCount++; return elementData.get(elementCount - 1); } public E pop() { if (elementCount == 0) { throw new StackOverflowError(); } E item = elementData.getAndSet(--elementCount, null); // 檢查容量,有多餘的就釋放掉 ensureCapacity(); return item; } public E peek() { if (elementCount == 0) { throw new StackOverflowError(); } return elementData.get(elementCount - 1); } public boolean empty() { return elementCount == 0; } public int search(Object o) { for (int i = 0; i < elementData.length(); i++) { E item = elementData.get(i); if (item == o || o.equals(item)) { return i; } } return -1; } /** * 判斷棧空間大小,不夠了就擴容,有多餘的就縮小 */ private void ensureCapacity() { if (elementCount == elementData.length()) { elementDataCopy = new AtomicReferenceArray<E>(elementCount + capacityIncrement); for (int i = 0; i < elementCount; i++) { elementDataCopy.set(i, elementData.get(i)); } elementData = elementDataCopy; }else if (elementCount == elementData.length() - capacityIncrement && elementCount > 0) { elementDataCopy = new AtomicReferenceArray<E>(elementData.length() - capacityIncrement); for (int i = 0; i < elementCount; i++) { elementDataCopy.set(i, elementData.get(i)); } elementData = elementDataCopy; } } }