CAS详解&线程安全

84 阅读2分钟

1.CAS实现原子操作

compare and swap

原子指令直接使用处理器的原子指令

Atomic类

java为了解决原子性操作引入的工具类

cas指令执行失败后,会重新获取当前值,一直循环,直到成功

public final int getAndAddInt(Object o, long offset, int delta) {
    int v;
    do {
        v = getIntVolatile(o, offset);
    } while (!weakCompareAndSetInt(o, offset, v, v + delta));
    return v;
}

1.1 CAS的ABA问题

线程1 执行将A变为C 线程2执行A变成C,C变成A,如果线程1在线程2执行完后再执行就会执行成功,这就是ABA问题;解决这个问题可以通过版本号来解决。

AtomicMarkableReference

这个解决了ABA问题只关心有没有被人改过

AtomicStampedReference

这个解决了ABA问题关心有没有被人改过,以及改过几次

1.2 循环时间长,开销大

大量线程高度竞争,CAS空挂占用的cpu时间多,反而导致

1.3 只能保证一个共享变量

AtomicReference封装成对象去实现多值的CAS

1.4 LongAdder

解决多个线程的写热点问题, 写冲突小的时候cas base,冲突多的时候,cas Cell数组;sum方法是一个估计值,不是准确值;sentinel 就使用了这个类

transient volatile long base;

transient volatile Cell[] cells;
public long sum() {
    Cell[] cs = cells;
    long sum = base;
    if (cs != null) {
        for (Cell c : cs)
            if (c != null)
                sum += c.value;
    }
    return sum;
}

2 线程安全性

2.1 线程封闭

栈封闭 尽量使用局部变量

ThreadLocal

无状态类 没有任何成员变量的类

类不可变

加锁和CAS

2.1 死锁和活锁

死锁与活锁区别是

死锁是处于 block状态,活锁是一直在获取锁释放锁处于 runnable;

活锁解决方法在释放锁后,休眠一个随机数,让一个线程拿到几把锁