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 }