Java 多執行緒高併發 2 — CAS 無鎖
阿新 • • 發佈:2018-12-11
在 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;
}
}
}