并发编程—CAS

CAS概念

CAS(Compare And Swap):比较并交换,CAS是一种原子操作,针对一个变量,首先比较它的内存值与某个期望值是否相同,如果相同,就给这个变量赋一个新值。CAS可以看做是乐观锁的一种实现,Java原子类中的递增操作就是通过CAS自旋实现的。CAS是一种无锁算法,在不使用锁(没有线程被阻塞)的情况下实现多线程之间的变量同步。

CAS应用

在 Java 中,CAS 操作是由 Unsafe 类提供支持的,该类定义了三种针对不同类型变量的 CAS 操作,如图:

它们都是 native 方法,由 Java 虚拟机提供具体实现,这意味着不同的 Java 虚拟机对它们的实现可能会略有不同。以compareAndSwapInt 为例,Unsafe 的 compareAndSwapInt方法接收 4 个参数,分别是:对象实例、内存偏移量、字段期望值、字段新值。该方法会针对指定对象实例中的相应偏移量的字段执行CAS 操作。

package com.warrior.juc.atomic;

import sun.misc.Unsafe;

import java.lang.reflect.Field;


/**
 * @Author warrior
 * CAS原子性操作案例
 */
public class CASDemo {

    public static void main(String[] args) {
        Entity entity = new Entity();

        Unsafe unsafe = UnsafeFactory.getUnsafe();

        long offset = UnsafeFactory.getFieldOffset(unsafe, Entity.class, "x");

        boolean successful;

        // 4个参数分别是:对象实例、字段的内存偏移量、字段期望值、字段新值
        successful = unsafe.compareAndSwapInt(entity, offset, 0, 3);
        System.out.println(successful + "\t" + entity.x);

        successful = unsafe.compareAndSwapInt(entity, offset, 3, 5);
        System.out.println(successful + "\t" + entity.x);

        successful = unsafe.compareAndSwapInt(entity, offset, 3, 8);
        System.out.println(successful + "\t" + entity.x);
    }

    static class UnsafeFactory {

        /**
         * 获取 Unsafe 对象
         *
         * @return
         */
        public static Unsafe getUnsafe() {
            try {
                Field field = Unsafe.class.getDeclaredField("theUnsafe");
                field.setAccessible(true);
                return (Unsafe) field.get(null);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }

        /**
         * 获取字段的内存偏移量
         *
         * @param unsafe
         * @param clazz
         * @param fieldName
         * @return
         */
        public static long getFieldOffset(Unsafe unsafe, Class clazz, String fieldName) {
            try {
                return unsafe.objectFieldOffset(clazz.getDeclaredField(fieldName));
            } catch (NoSuchFieldException e) {
                throw new Error(e);
            }
        }
    }

    static class Entity {
        int x;
    }
}
CAS缺陷

CSA虽然高效的解决了原子操作,但是还是存证一些缺陷的,主要表现在三个方面:

  • 自旋CAS长时间地不成功,则会给CPU带来非常大的开销。

  • 只能保证一个共享变量的原子操作

  • ABA问题

ABA
ABA问题

当有多个线程对一个原子类进行操作的时候,某个线程在短暂的时间内将原子类的值A修改为B,又马上将其修改为A,此时其他线程不感知,还是会修改成功。

package com.warrior.juc.atomic;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * @Author warrior
 * ABA问题案例
 */
public class ABADemo {

    private static AtomicInteger atomicInteger = new AtomicInteger(1);

    public static void main(String[] args) {

        new Thread(() -> {
            int value = atomicInteger.get();
            System.out.println(Thread.currentThread().getName() + " read value:" + value);

            //休眠1s
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            //CAS修改value值
            boolean success = atomicInteger.compareAndSet(value,3);
            if(success){
                System.out.println(Thread.currentThread().getName() + " update from "+ value + " to 3 success");
            }else {
                System.out.println(Thread.currentThread().getName() + " update from "+ value + " to 3 fail");
            }
        }, "Thread-1").start();

        new Thread(() -> {
            int value = atomicInteger.get();
            System.out.println(Thread.currentThread().getName() + " read value:" + value);

            //CAS修改value值
            boolean success = atomicInteger.compareAndSet(value,2);
            if(success){
                System.out.println(Thread.currentThread().getName() + " update from "+ value + " to 2 success");
                value = atomicInteger.get();
                System.out.println(Thread.currentThread().getName() + " read value:" + value);
                success = atomicInteger.compareAndSet(value,1);
                if(success){
                    System.out.println(Thread.currentThread().getName() + " update from "+ value + " to 1 success");
                }
            }
        },"Thread-2").start();

    }

}

输出结果:

Thread-1 read value:1
Thread-2 read value:1
Thread-2 update from 1 to 2 success
Thread-2 read value:2
Thread-2 update from 2 to 1 success
Thread-1 update from 1 to 3 success
ABA解决方案

数据库有个锁称为乐观锁,是一种基于数据库版本实现的数据同步机制,每次修改一次数据,版本就会进行累加。同样,Java也提供了相应的原子引用类AtomicStampedReference<V>

reference即我们实际存储的变量,stamp是版本,每次修改可以通过+1保证版本唯一性。这样就可以保证每次修改后的版本也会往上递增。
package com.warrior.juc.atomic;

import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicStampedReference;

/**
 * @Author warrior
 * ABA问题解决方案案例
 */
public class AtomicStampedReferenceDemo {

    // 定义AtomicStampedReference    Pair.reference值为1, Pair.stamp为1
    static AtomicStampedReference atomicStampedReference = new AtomicStampedReference(1,1);

    public static void main(String[] args) {

        new Thread(() -> {
            int[] stampHolder = new int[1];
            int value = (int) atomicStampedReference.get(stampHolder);
            int stamp = stampHolder[0];
            System.out.println(Thread.currentThread().getName() + " read value:" + value);

            //休眠1s
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            //CAS修改value值
            boolean success = atomicStampedReference.compareAndSet(value,3,stamp,stamp+1);
            if(success){
                System.out.println(Thread.currentThread().getName() + " update from "+ value + " to 3 success");
            }else {
                System.out.println(Thread.currentThread().getName() + " update from "+ value + " to 3 fail");
            }
        }, "Thread-1").start();

        new Thread(() -> {
            int[] stampHolder = new int[1];
            int value = (int) atomicStampedReference.get(stampHolder);
            int stamp = stampHolder[0];
            System.out.println(Thread.currentThread().getName() + " read value:" + value);

            //CAS修改value值
            boolean success = atomicStampedReference.compareAndSet(value,2,stamp,stamp+1);
            if(success){
                System.out.println(Thread.currentThread().getName() + " update from "+ value + " to 2 success");
                 value = (int) atomicStampedReference.get(stampHolder);
                 stamp = stampHolder[0];
                System.out.println(Thread.currentThread().getName() + " read value:" + value);
                success = atomicStampedReference.compareAndSet(value,1,stamp,stamp+1);
                if(success){
                    System.out.println(Thread.currentThread().getName() + " update from "+ value + " to 1 success");
                }
            }
        },"Thread-2").start();

    }
}

输出结果:

Thread-1 read value:1
Thread-2 read value:1
Thread-2 update from 1 to 2 success
Thread-2 read value:2
Thread-2 update from 2 to 1 success
Thread-1 update from 1 to 3 fail
AtomicMarkableReference可以理解为上面AtomicStampedReference的简化版,就是不关心修改过几次,仅仅关心是否修改过。因此变量mark是boolean类型,仅记录值是否有过修改

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值