JDK8新增

LongAdder數組

提供了原子累計值的方法。緩存

 

在高併發下N多線程同時去操做一個變量會形成大量線程CAS失敗而後處於自旋狀態,這大大浪費了cpu資源,下降了併發性。那麼既然AtomicLong性能因爲過多線程同時去競爭一個變量的更新而下降的,LongAdder思路把一個變量分解爲多個變量,讓一樣多的線程去競爭多個資源那麼性能問題獲得解決多線程

 

LongAdder extends Striped64 implements Serializable併發

 

increment() / decrement()高併發

 1     public void increment() {
 2         add(1L);
 3     }
 4 
 5     /**
 6      * Equivalent to {@code add(-1)}.
 7      */
 8     public void decrement() {
 9         add(-1L);
10     }

 

add()性能

 1     public void add(long x) {
 2         Cell[] as; long b, v; int m; Cell a;
 3         if ((as = cells) != null || !casBase(b = base, b + x)) {
 4             boolean uncontended = true;
 5             if (as == null || (m = as.length - 1) < 0 ||
 6                 (a = as[getProbe() & m]) == null ||
 7                 !(uncontended = a.cas(v = a.value, v + x)))
 8                 longAccumulate(x, null, uncontended);
 9         }
10     }

LongAdder維護了一個延遲初始化的原子性更新數組和一個基值變量base.數組的大小保持是2的N次方大小,數組表的下標使用每一個線程的hashcode值的掩碼錶示,數組裏面的變量實體是Cell類型,Cell類型是AtomicLong的一個改進,用來減小緩存的爭用,對於大多數原子操做字節填充是浪費的,由於原子性操做都是無規律的分散在內存中進行的,多個原子性操做彼此之間是沒有接觸的,可是原子性數組元素彼此相鄰存放將能常常共享緩存行,因此這在性能上是一個提高。ui

另外因爲Cells佔用內存是相對比較大的,因此一開始並不建立,而是在須要時候在建立,也就是惰性加載,當一開始沒有空間時候,全部的更新都是操做base變量,this

 

 

 

 

StampedLockspa

ReadWriteLock 寫鎖是互斥的線程

讀-寫

寫-寫

ReentrantReadWriteLock是讀寫鎖,在多線程環境下,大多數狀況是讀的狀況遠遠大於寫的操做,所以可能致使寫的飢餓問題

 

StampedLock

讀鎖並不會阻塞寫鎖,讀取失敗後從新讀

 

writeLock()

寫鎖writeLock是一個獨佔鎖,同時只有一個線程能夠獲取該鎖,當一個線程獲取該鎖後,其餘請求讀鎖和寫鎖的線程必須等待,這跟ReentrantReadWriteLock 的寫鎖很類似,不過要注意的是StampedLock的寫鎖是不可重入鎖,

當目前沒有線程持有讀鎖或者寫鎖的時候才能夠獲取到該鎖,請求該鎖成功後會返回一個stamp 票據變量來表示該鎖的版本

1     public long writeLock() {
2         long s, next;  // bypass acquireWrite in fully unlocked case only
3         return ((((s = state) & ABITS) == 0L &&
4                  U.compareAndSwapLong(this, STATE, s, next = s + WBIT)) ?
5                 next : acquireWrite(false, 0L));
6     }

 

readLock()

是個共享鎖,在沒有線程獲取獨佔寫鎖的狀況下,同時多個線程能夠獲取該鎖;若是已經有線程持有寫鎖,其餘線程請求獲取該鎖會被阻塞,這相似ReentrantReadWriteLock 的讀鎖(不一樣在於這裏的讀鎖是不可重入鎖)。

這裏說的悲觀是指在具體操做數據前,悲觀的認爲其餘線程可能要對本身操做的數據進行修改,因此須要先對數據加鎖,這是在讀少寫多的狀況下的一種考慮,請求該鎖成功後會返回一個stamp票據變量來表示該鎖的版本

    / * Non-exclusively acquires the lock, blocking if necessary
     * until available.
     *
     * @return a stamp that can be used to unlock or convert mode
     */
    public long readLock() {
        long s = state, next;  // bypass acquireRead on common uncontended case
        return ((whead == wtail && (s & ABITS) < RFULL &&
                 U.compareAndSwapLong(this, STATE, s, next = s + RUNIT)) ?
                next : acquireRead(false, 0L));
    }

 樂觀鎖失敗後鎖升級爲readLock():嘗試state+1,用於統計讀線程的數量,若是失敗,進入acquireRead()進行自旋,經過CAS獲取鎖

 

舉例

 1 public class Demo {
 2     
 3     private int balance;
 4     
 5     private StampedLock lock = new StampedLock();
 6     
 7     public void conditionReadWrite (int value) {
 8         
 9         // 首先判斷balance的值是否符合更新的條件
10         long stamp = lock.readLock();
11         while (balance > 0) {
12             long writeStamp = lock.tryConvertToWriteLock(stamp);
13             if(writeStamp != 0) { // 成功轉換成爲寫鎖
14                 stamp = writeStamp;
15                 balance += value;
16                 break;
17             } else {
18                 // 沒有轉換成寫鎖,這裏須要首先釋放讀鎖,而後再拿到寫鎖
19                 lock.unlockRead(stamp);
20                 // 獲取寫鎖
21                 stamp = lock.writeLock();
22             }
23          }
24         
25         lock.unlock(stamp);
26     }
27     
28     public void optimisticRead() {
29         long stamp = lock.tryOptimisticRead();
30         int c = balance;
31         // 這裏可能會出現了寫操做,所以要進行判斷
32         if(!lock.validate(stamp)) {
33             // 要重新讀取
34             long readStamp = lock.readLock();
35             c = balance;
36             stamp = readStamp; //更新票據,從而釋放
37         }
38         /// 
39         lock.unlockRead(stamp);
40     }
41     
42     public void read () {
43         long stamp = lock.readLock();
44         lock.tryOptimisticRead();
45         int c = balance;
46         // ...
47         lock.unlockRead(stamp);
48     }
49     
50     public void write(int value) {
51         long stamp = lock.writeLock();
52         balance += value;
53         lock.unlockWrite(stamp);
54     }
55     
56 
57 }
相關文章
相關標籤/搜索