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的一个改进,用来减少缓存的争用,对于大多数原子操作字节填充是浪费的,因为原子性操作都是无规律的分散在内存中进行的,多个原子性操作彼此之间是没有接触的,但是原子性数组元素彼此相邻存放将能经常共享缓存行,所以这在性能上是一个提升。

另外由于Cells占用内存是相对比较大的,所以一开始并不创建,而是在需要时候在创建,也就是惰性加载,当一开始没有空间时候,所有的更新都是操作base变量,

 

 

 

 

StampedLock

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 }

 












 

posted @ 2019-07-20 01:31  曲阳阳  阅读(234)  评论(0编辑  收藏  举报