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 }