并发编程—3Cas原子操作

3.CAS原子操作

什么是原子性操作,按照官方的解析:原子操作不能在一个中间操作中停止,要么全部成功要么全部失败。(An atomic action cannot stop in the middle: it either happens completely, or it doesn't happen at all. No side effects of an atomic action are visible until the action is complete.),

-- 除了long和double的读写,其他的引用变量的读写操作都是原子性的
-- 如果加了volatile关键字,所有的引用变量的读写操作都是原子性的。
-- 原子操作不会被其他的线程干扰。但是会存在内存不一致(consistency memory)的问题。但是使用关键字volatile可以大大减少这个风险。
-- 线程修改volatile变量的时候,会在这个变量上面和其他的读取该变量的读线程上面建立一个happens-before的关系。保证写线程对该变量的修改对其他的读线程是可见的。

compare and swap.在一个内存地址V,一个期望值A,一个新值B。如果在V上面是期望的A值,那么用B值去替换。否则通过自旋方式(死循环)一直到找到期望值位置。

3.1 为什么要有CAS,传统的锁有什么弊端

传统的锁实现比较笨重,如果程序中需要对某些计算变量实现原子性,是用java的锁会很笨重。而且给资源加锁如果控制不得当的话,容易出现死锁的现象。而CAS的出现就是为了解决在无锁的情况下也可以实现对于
资源的原子操作。

3.2 CAS的问题

+ CAS容易产生ABA问题,就是一个内存地址V,值是A。有一个a线程要去修改它的值,此时有一个b线程把A的值改为了B,然后再改回A,此时对于a线程来说,它对于b线程的修改操作时无感知的。为了解决ABA问题,引入了版本号。所有
线程修改一个内存的值前要先获取值的版本号,修改完后需要更新版本号。这似乎就需要使用AtomicStampedReference来操作了。如果是不关心值是否被修改过的情况,不需要考虑ABA问题。
+ 因为CAS会以自旋的方式索引期望值,如果一直没有索引到期望值。为了防止一直自旋下去,cpu会设置一定的阈值,超过阈值后就会挂起这个线程,让出cpu。

3.3 举例

/**
 * 演示带版本Stamp的AtomicStampedReference使用
 * compareAndSet方法,需要指定老的版本号,如果设置成功返回true,否则返回false
 * @author 45027056
 *
 */
public class UseAtomicReferenceWithStamp {
    static String name = "luke";
    AtomicStampedReference atomicStampedReference = new AtomicStampedReference(name,0);
    public static void main(String[] args) {
        UseAtomicReferenceWithStamp demo = new UseAtomicReferenceWithStamp();
        demo.new SuccessThread().start();
        de mo.new FailThread().start();
        System.out.println("local name is...:"+ name);
    }
    
    class SuccessThread extends Thread{
        int oldStamp = atomicStampedReference.getStamp();
        String oldReferenct = (String) atomicStampedReference.getReference();
        @Override
        public void run() {
            boolean result = atomicStampedReference.compareAndSet(oldReferenct, "joe1", 0, 1);
            System.out.println("refernce is :" + atomicStampedReference.getReference());
            System.out.println("stamp is :" + atomicStampedReference.getStamp());
            System.out.println("result is :" + result);
        }
    }
    
    class FailThread extends Thread{
        int oldStamp = atomicStampedReference.getStamp();
        String oldReferenct = (String) atomicStampedReference.getReference();
        @Override
        public void run() { 
            boolean result = atomicStampedReference.compareAndSet(oldReferenct, "joe2", 0, 1);
            System.out.println("refernce is" + atomicStampedReference.getReference());
            System.out.println("stamp is" + atomicStampedReference.getStamp());
            System.out.println("result is :" + result);
        }
    }
}

猜你喜欢

转载自www.cnblogs.com/codetree/p/10884160.html