java-CAS 原理

最近感兴趣的内容到J.U.C包中了,当然了,对于这个包我的理解还是只看见大门,首先,自己没机会在项目中实战,其次,平时很少练手,再其次,最近才看,所以很多文章都是网上大牛写的,自己翻译过来,就当做逼自己重新看一遍了。

首先谈谈自己对于锁的恐惧吧,其实对于每个初级人员来说,锁是一个高深的问题,一般情况下我估计所有的初级人员来说的话,都很少使用,即使使用了,也是简单的synchronized吧,对于其他的锁我估计都很少使用,笔者最近使用了redis锁,说来其实很讽刺啊,单服务器的锁还没玩的溜,都开始装逼使用redis锁了,hh。

我对于使用synchronized的理解:

1) 当前线程持有锁的话,其他线程就挂起了。

2) 如果在有竞争的情况下呢?加锁和释放锁肯定会带来性能上的消耗吧。

3) 如果一个线程的优先级高,但是确在等待一个优先级低的线程释放锁,是否会有问题(这个只是猜测了,本人其实不知道),纯粹来源于网络知识。

两种锁:悲观锁和乐观锁。

synchronized就是一个悲观锁,会导致所有的线程等待当前线程释放锁之后才能进入。

乐观锁就是每次不加锁而是假设没有冲突去完成某项操作,如果因为冲突失败了就重试,直至成功,乐观锁的一种实现就是CAS Compare And Swap.


CAS有三个操作数----内存旧值(V),预期原值(A) 和新值(B)。CAS 有效地说明了“我认为位置 V 应该包含值 A;如果包含该值,则将 B 放到这个位置;否则,不要更改该位置,只告诉我这个位置现在的值即可。


CAS利用了CPU的CAS指令和 JNI的来完成java的非阻塞算法。

关于ABA问题不去记录了,现在已近不存在这个问题了。


具体来看AtomicInteger这个类。Unsafe这个类就是java代码和计算机底层进行交互时使用的代码。这个我不想关注,知道即可。看其中的valueOffset是什么?CAS是拿期望的值和原本的值作比较,相同的话就更新为新值,那这个valueOffset其实就是原本的值。这个方法拿到的就是一个原本的值的内存地址(为什么要看内存地址,就是去解决ABA问题的)。

// setup to use Unsafe.compareAndSwapInt for updates
    private static final Unsafe unsafe = Unsafe.getUnsafe();
    private static final long valueOffset;

    static {
      try {
        valueOffset = unsafe.objectFieldOffset
            (AtomicInteger.class.getDeclaredField("value"));
      } catch (Exception ex) { throw new Error(ex); }
    }
value是volatile的,volatile变量在内存中对任意线程都是可见的,但是它不能保证原子性

private volatile int value;

来看看这个方法。内部是个无线循环,这就是上文提到的乐观锁,假设没有竞争,每个程序都是在无线循环,直至条件满足,return掉。

current和next这两个操作都是非线程安全的,关键的就在于compareAndSet这个方法。

    /**
     * Atomically increments by one the current value.
     *
     * @return the previous value
     */
    public final int getAndIncrement() {
        for (;;) {
            int current = get();
            int next = current + 1;
            if (compareAndSet(current, next))
                return current;
        }
    }
这边对于get方法来说只传入了current和next的值,但是底层计算时并不是这样,this,当前对象的地址,valueOffset的为原本值的相对对象的偏移量,expect为期望的值,update为新值,也就是expect的值和valueOffset处的值相等时,更新为新值。
/**
     * Atomically sets the value to the given updated value
     * if the current value {@code ==} the expected value.
     *
     * @param expect the expected value
     * @param update the new value
     * @return true if successful. False return indicates that
     * the actual value was not equal to the expected value.
     */
    public final boolean compareAndSet(int expect, int update) {
        return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }

其他方法多多少少都是基于此,就不一一看了。

但是AtomicInteger并不能来测试啊,看不见输出啊, 看不见每个线程的旧值,新值啊。怎么办呢?自己实现一个呗。

首先要结局unsafe这个玩意儿吧,至于怎么获取,www.baidu.com,可以找到吧,好像只提供了一个获取方式,反射获取。

public class UnsafeFactory {

    private static Unsafe unsafe = null;

    static {
        try {
            Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");
            unsafeField.setAccessible(true);
            unsafe = (Unsafe) unsafeField.get(null);
        } catch (Exception e) {
            e.printStackTrace();
            throw new Error("unsafe init error");
        }
    }

    public static Unsafe get() {
        return unsafe;
    }

}
自己实现的,参考AtomicInteger就行了,没什么难处

public class UserAtomicInteger {

    private static final Unsafe unsafe = UnsafeFactory.get();
    private volatile int value;

    private static final long valueOffset;

    static {
        try {
            valueOffset = unsafe.objectFieldOffset(UserAtomicInteger.class
                    .getDeclaredField("value"));
        } catch (Exception ex) {
            throw new Error(ex);
        }
    }

    public UserAtomicInteger(int initialValue) {
        value = initialValue;
    }

    public void compareAndSwap(){
        for(;;){
            try{
                System.out.println(Thread.currentThread()+"当前值为: "+value);
                int oldValue = value;
                int newValue = oldValue + 1;
                if(compareAndSet(oldValue,newValue)){
                    System.out.println("线程: "+ Thread.currentThread() + "赋值成功。value: "+ value+ " old : "+ oldValue + " new : "+ newValue);
                }
            }catch (Exception e){
                System.out.println(e.getMessage());
            }
        }
    }

    private boolean compareAndSet(int expect, int update) {
        return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }

}


测试类,起4个线程,while一直去运行。

public class TestAtomic {
    public static void main(String[] args) {
        UserAtomicInteger a = new UserAtomicInteger(1);

        MyThread t1 = new MyThread(a);
        MyThread t2 = new MyThread(a);
        MyThread t3 = new MyThread(a);
        MyThread t4 = new MyThread(a);

        t1.start();
        t2.start();
        t3.start();
        t4.start();

    }
}

class MyThread extends Thread{

    UserAtomicInteger use;

    public MyThread(UserAtomicInteger use){
        this.use = use;
    }

    @Override
    public void run() {
        try{
            while (true){
               use.compareAndSwap();
            }
        }catch (Exception e){
            e.getMessage();
        }
    }
}

可以看见输出,

看见第5行和第7行,线程1读取到为3,线程3读取到也为3,但是线程1进入了CAS过程,然后值为4了,线程3虽然读到了是3,但是此时值已变,最新值是4

Thread[Thread-0,5,main]当前值为: 1
线程: Thread[Thread-0,5,main]赋值成功。value: 2 old : 1 new : 2
Thread[Thread-2,5,main]当前值为: 2
线程: Thread[Thread-2,5,main]赋值成功。value: 3 old : 2 new : 3
Thread[Thread-1,5,main]当前值为: 3
线程: Thread[Thread-1,5,main]赋值成功。value: 4 old : 3 new : 4
Thread[Thread-3,5,main]当前值为: 3
线程: Thread[Thread-3,5,main]赋值成功。value: 5 old : 4 new : 5
Thread[Thread-0,5,main]当前值为: 5
线程: Thread[Thread-0,5,main]赋值成功。value: 6 old : 5 new : 6
Thread[Thread-2,5,main]当前值为: 6
线程: Thread[Thread-2,5,main]赋值成功。value: 7 old : 6 new : 7
Thread[Thread-3,5,main]当前值为: 7
线程: Thread[Thread-3,5,main]赋值成功。value: 8 old : 7 new : 8
Thread[Thread-1,5,main]当前值为: 7
线程: Thread[Thread-1,5,main]赋值成功。value: 9 old : 8 new : 9
Thread[Thread-0,5,main]当前值为: 9
线程: Thread[Thread-0,5,main]赋值成功。value: 10 old : 9 new : 10
Thread[Thread-2,5,main]当前值为: 10
线程: Thread[Thread-2,5,main]赋值成功。value: 11 old : 10 new : 11
Thread[Thread-1,5,main]当前值为: 11
线程: Thread[Thread-1,5,main]赋值成功。value: 12 old : 11 new : 12
Thread[Thread-3,5,main]当前值为: 11
线程: Thread[Thread-3,5,main]赋值成功。value: 13 old : 12 new : 13
Thread[Thread-0,5,main]当前值为: 13
线程: Thread[Thread-0,5,main]赋值成功。value: 14 old : 13 new : 14
Thread[Thread-2,5,main]当前值为: 14
线程: Thread[Thread-2,5,main]赋值成功。value: 15 old : 14 new : 15
Thread[Thread-3,5,main]当前值为: 15
线程: Thread[Thread-3,5,main]赋值成功。value: 16 old : 15 new : 16
Thread[Thread-1,5,main]当前值为: 16
线程: Thread[Thread-1,5,main]赋值成功。value: 17 old : 16 new : 17
Thread[Thread-0,5,main]当前值为: 17
线程: Thread[Thread-0,5,main]赋值成功。value: 18 old : 17 new : 18
Thread[Thread-2,5,main]当前值为: 18
线程: Thread[Thread-2,5,main]赋值成功。value: 19 old : 18 new : 19
Thread[Thread-1,5,main]当前值为: 19
线程: Thread[Thread-1,5,main]赋值成功。value: 20 old : 19 new : 20
Thread[Thread-3,5,main]当前值为: 20
线程: Thread[Thread-3,5,main]赋值成功。value: 21 old : 20 new : 21


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值