RxJavaJava服务器端编程编程语言爱好者

并发编程-CAS无锁模式及ABA问题

2020-11-28  本文已影响0人  迦叶_金色的人生_荣耀而又辉煌

上一篇 <<<Volatile的伪共享和重排序
下一篇 >>>Synchronized锁


CAS(Compare and Swap):比较并交换
优势: 非阻塞性,它对死锁问题天生免疫,使用无锁的方式完全没有锁竞争带来的系统开销,也没有线程间频繁调度带来的开销。

1.Cas 是通过硬件指令实现,保证原子性
2.Java是通过unsafe jni技术
3.原子类: AtomicBoolean,AtomicInteger,AtomicLong 等使用 CAS 实现。

CAS(V,E,N)

操作机制:【(本地内存值==主内存预期值)?新值更新:迭代循环等待(也叫自旋)】
tips: CAS是通过自旋实现的,但不能说CAS就是自旋锁。

CAS底层源码

// 调用方法
atomic.incrementAndGet();
/*底层源码:*/
public final int getAndAddInt(Object var1, long var2, int var4) {
    int var5;
    do {
        var5 = this.getIntVolatile(var1, var2);
  //这里就是循环自旋
    } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));

    return var5;
}

/*以前的写法: */
public final int incrementAndGet() {  
    for (;;) {  
        //获取当前值  
        int current = get();  
        //设置期望值  
        int next = current + 1;  
        //调用Native方法compareAndSet,执行CAS操作  
        if (compareAndSet(current, next))  
            //成功后才会返回期望值,否则无线循环  
            return next;  
    }  
}

CAS的ABA问题

刚开始读取的时候是A,更新的时候也是A,但中间可能会被改为B过了。

CAS的ABA问题解决办法

方案1:AtomicStampedReference

java并发包中提供了一个带有标记的原子引用类AtomicStampedReference,它可以通过控制变量值的版本来保证CAS的正确性。

/**
 * 使用AtomicStampedReference可获取版本号信息
 * 通过版本号可解决ABA问题
 */
atomicStampedReference = new AtomicStampedReference(INIT_NUM, 1);

Integer value = (Integer) atomicStampedReference.getReference();
int stamp = atomicStampedReference.getStamp();
System.out.println(Thread.currentThread().getName() + " : 当前值为:" + value + " 版本号为:" + stamp);
try {
    Thread.sleep(1000);
} catch (InterruptedException e) {
    e.printStackTrace();
}
//(1)第一个参数expectedReference:表示预期值。
//(2)第二个参数newReference:表示要更新的值。
//(3)第三个参数expectedStamp:表示预期的时间戳。
//(4)第四个参数newStamp:表示要更新的时间戳。
if (atomicStampedReference.compareAndSet(value, UPDATE_NUM, 1, stamp + 1)) {
    System.out.println(Thread.currentThread().getName() + " : 当前值为:" + atomicStampedReference.getReference() + " 版本号为:" + atomicStampedReference.getStamp());
} else {
    System.out.println("版本号不同,更新失败!");
}

方案2:利用原子类手写CAS无锁

/**
 * 定义AtomicInteger  修改为1表示该锁已经被使用该 修改为0表示为被使用
 */
private volatile AtomicInteger atomicInteger = new AtomicInteger(0);
private Thread lockCurrentThread;

/**
 * 尝试获取锁
 *
 * @return
 */
public boolean tryLock() {
    boolean result = atomicInteger.compareAndSet(0, 1);
    if (result) {
        lockCurrentThread = Thread.currentThread();
    }
    return result;
}

/**
 * 释放锁
 *
 * @return
 */
public boolean unLock() {
    if (lockCurrentThread != null && lockCurrentThread != Thread.currentThread()) {
        return false;
    }
    return atomicInteger.compareAndSet(1, 0);
}

Java中Unsafe实现cas无锁机制常用方法

try {
        Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
        theUnsafe.setAccessible(true);
        unsafe = (Unsafe) theUnsafe.get(null);
    } catch (Exception e) {
        e.printStackTrace();
    }
// o csa对象  offset内存偏移量  expected 预期值  x新值
public final native boolean compareAndSwapInt(Object o, long offset,int expected,int x);
//获取内存偏移量
public native long objectFieldOffset(Field f);
//park 是挂起当前线程 isAbsolute 为true 代表睡眠一段时自动唤醒 time是纳秒级别
public native void park(boolean isAbsolute, long time);
//unpark 将此线程唤醒
public native void unpark(Object thread);

相关文章链接:
多线程基础
线程安全与解决方案
锁的深入化
锁的优化
Java内存模型(JMM)
Volatile解决JMM的可见性问题
Volatile的伪共享和重排序
Synchronized锁
Lock锁
AQS同步器
Condition
CountDownLatch同步计数器
Semaphore信号量
CyclicBarrier屏障
线程池
并发队列
Callable与Future模式
Fork/Join框架
Threadlocal
Disruptor框架
如何优化多线程总结

上一篇 下一篇

猜你喜欢

热点阅读