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 }
相关文章
相关标签/搜索