CAS基本原理

CAS(Compare And Swap)

Atomic开头的类都被称为原子变量类
原子操作:不可再分的操作,这个操作要么全部完成,要么全不完成。
synchronized关键字包围的代码块就是一个原子操作
synchronized可以实现原子操作,但这个机制太重

CAS的原理

利用了现代处理器都支持的CAS的指令,
循环这个指令,直到成功为止
在这里插入图片描述

悲观锁和乐观锁:

Synchronized 是悲观锁 ,抢到锁后安安心心的做。
CAS 是乐观锁,先取出来再去改 ,用CAS指令去比较和交换(比较没人改过就交换,有人改了就再来一遍)

性能比较

大部分情况,原子变量要比加锁性能高
Synchronized 关键字加锁,多个线程竞争的时候,任意时刻只有一个线程能进入锁范围内,其他没有竞争成功的线程被阻塞,被阻塞会发生上下文切换,一次上下文切换牵涉的资源很多(一次上下文切换大约5000-20000个时钟周期,3-5ms)
CAS的一条指令0.6ns,算上自旋翻几十倍 600ns也比上下文切换快的多,而且上下文切换在唤醒时还会发生一次(一次拿锁至少发生两次上下文切换3-5ms*2)
整个JDK并发编程发展趋势-CAS机制,无锁化编程
高度竞争,特意设计的情况下Synchronized 性能好些,这种情况在目前生产中几乎是没有的。

CAS的问题

CAS这么强,为什么加锁的机制还有生存的余地?

1.ABA问题:
线程1: 拿到A,比较判断是不是A,是的话换成B
线程2跑的更快:拿到A,比较是A换成C又迅速换成A
当线程1比较的时候是A,以为没人改过,实际上被改过。
这就是ABA问题

ABA问题解决方法:
加一个版本戳
AtomicMarkableReference关心这个变量有没有人动过
AtomicStampedReference被动了几次

/**
 *类说明:演示带版本戳的原子操作类
 */
public class UseAtomicStampedReference {
    static AtomicStampedReference<String> asr
            = new AtomicStampedReference("puppy",0);

    public static void main(String[] args) throws InterruptedException {
        //拿到当前的版本号(旧)
        final int oldStamp = asr.getStamp();
        final String oldReference = asr.getReference();
        System.out.println(oldReference+"  ---------------   "+oldStamp);

        Thread rightStampThread = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+":  当前变量值:"
                        +oldReference + "     当前版本戳:" + oldStamp + "     "
                  + asr.compareAndSet(oldReference,
                        oldReference + "love", oldStamp,
                        oldStamp + 1));
            }
        });

        Thread errorStampThread = new Thread(new Runnable() {
            @Override
            public void run() {
                String reference = asr.getReference();
                System.out.println(Thread.currentThread().getName()
                        +":  当前变量值:"
                        +reference + "   当前版本戳:" + asr.getStamp() + "    "
                        + asr.compareAndSet(reference,
                        reference + "donot", oldStamp,
                        oldStamp + 1));
            }
        });
        rightStampThread.start();
        rightStampThread.join();
        errorStampThread.start();
        errorStampThread.join();

        System.out.println(asr.getReference()+"   -------------   "+asr.getStamp());
    }
}
puppy  ---------------   0
Thread-0:  当前变量值:puppy     当前版本戳:0     true
Thread-1:  当前变量值:puppylove   当前版本戳:1    false
puppylove   -------------   1

2.开销问题:
较不相等则自旋 循环的次数长期不成功 那么开销大。

开销问题解决方法:
开销特别大,要么就改成加锁

3.只能保证一个共享变量的原子操作:
一个地址保证一个变量,现在cpu指令进行CAS修改时,只能针对某个地址进行修改,如果希望多个变量的修改是个原子操作,则CAS不能适应,就得使用加锁机制

只能保证一个共享变量的原子操作解决方法:
AtomicReference 解决只能保证一个共享变量的原子操作,把两个变量组合到一个对象中去

/**
 *类说明:演示引用类型的原子操作类
 */
public class UseAtomicReference {
    static AtomicReference<UserInfo> atomicUserRef;
    public static void main(String[] args) {
        UserInfo user = new UserInfo("puppy", 14);//要修改的实体的实例
        atomicUserRef = new AtomicReference(user);
        UserInfo updateUser = new UserInfo("puppylove",17);
        atomicUserRef.compareAndSet(user,updateUser);

        System.out.println(atomicUserRef.get());
        System.out.println(user);
    }
    
    //定义一个实体类
    static class UserInfo {
        private volatile String name;
        private int age;
        public UserInfo(String name, int age) {
            this.name = name;
            this.age = age;
        }
        public String getName() {
            return name;
        }
        public int getAge() {
            return age;
        }

        @Override
        public String toString() {
            return "UserInfo{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    '}';
        }
    }

}
UserInfo{name='puppylove', age=17}
UserInfo{name='puppy', age=14}

CAS的使用,JDK提供了很多原子操作类:
Jdk中相关原子操作类的使用
更新基本类型类:AtomicBoolean,AtomicInteger,AtomicLong
更新数组类:AtomicIntegerArray,AtomicLongArray,AtomicReferenceArray
更新引用类型:AtomicReference,AtomicMarkableReference,AtomicStampedReference

/**
 *类说明:演示基本类型的原子操作类
 */
public class UseAtomicInt {
    static AtomicInteger ai = new AtomicInteger(10);

    public static void main(String[] args) {
        ai.getAndIncrement();//i++

        ai.incrementAndGet();//++i

        ai.addAndGet(24);//+24 返回加的新值

        ai.getAndAdd(24);//+24 返回加的旧值

        System.out.println(ai);
    }
}
60
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值