Java CAS(Compare And Swap)的一点理解

CAS: Compare And Swap, 即比较和替换。

思想: CAS的比较、替换操作是非阻塞操作, 它有3个参数分别为内存值、预期值和更新值。 当内存值和预期值匹配时则更新, 不匹配时直接返回。

PS: CAS比synchronized效率要好,  因为CAS是c语言实现的cpu锁机制, synchronized是Java锁。

        简单的说CAS提供了一种实现原子操作的方法, 都封装在sun.misc.Unsafe类里。 而在实际开发过程中, 并不建议直接操作Unsafe类, 而且使用基于Unsafe类实现的各个派生类。



     这些类都提供了原子操作, 假设使用AtomicInteger模拟生产者消费者模式。 

 AtomicInteger object = new AtomicInteger(0);
        for (int i = 0; i < 4; i++) {
            //逻辑上的生产者, 值从0到1
            new Thread() {
                @Override
                public void run() {
                    while (true) {
                        try {
                            Thread.sleep(100);
                        } catch (Exception ex) {
                        }
                        System.out.println("判断0线程,当前值是"+ object.get()
                                + ", 返回值"+ object.compareAndSet(0, 1));   //compare函数是非阻塞
                    }
                }
            }.start();

            //逻辑上的消费者, 值从1到0
            new Thread() {
                @Override
                public void run() {
                    while (true) {
                        try {
                            Thread.sleep(100);
                        } catch (Exception ex) {
                        }
                        System.out.println("判断1线程,当前值是" + object.get()
                                + ", 返回值" +  object.compareAndSet(1, 0));  //非阻塞
                    }
                }
            }.start();
        }
     4个生产者线程、4个开发者线程。 生产者线程的作用是0到1, 消费者线程的作用是1到0。 代码里并没有同步控制逻辑(即没有synchronized/Lock), 运行程序看看效果。
判断0线程,当前值是0, 返回值true
判断0线程,当前值是0, 返回值true
判断1线程,当前值是0, 返回值false
判断1线程,当前值是1, 返回值true
判断0线程,当前值是0, 返回值true
判断1线程,当前值是1, 返回值true
判断1线程,当前值是1, 返回值true
判断0线程,当前值是1, 返回值false
判断0线程,当前值是0, 返回值true
判断0线程,当前值是1, 返回值false
判断1线程,当前值是1, 返回值true
判断1线程,当前值是0, 返回值false
判断0线程,当前值是0, 返回值true
判断1线程,当前值是1, 返回值true
        程序运行正常, 并没有报共享冲突错误。
原理:

1、 volatile关键字保证这8个线程操作的是同一块内存;

2、 原子性操作compareAndSet即比较并更新, 是在native层实现的, 原理见下面的2篇博客。

扫描二维码关注公众号,回复: 2853165 查看本文章



CAS里所有ABA问题其实就是值在改回来前不知道发生了什么,  例如: 你读取的值是1, 但前面可能经历了3、2等值的情况。 


使用AtomicStampedReference可以解决ABA问题, 因为每次更新值时都有一个时间戳。 在compareAndSet时既要比较值,也要比较时间戳是否一致。


    public boolean compareAndSet(V   expectedReference,
                                 V   newReference,
                                 int expectedStamp,
                                 int newStamp) {
        Pair<V> current = pair;
        return
            expectedReference == current.reference &&
            expectedStamp == current.stamp &&
            ((newReference == current.reference &&
              newStamp == current.stamp) ||
             casPair(current, Pair.of(newReference, newStamp)));
    }


参考博客:

https://www.jianshu.com/p/24ffe531e9ee

http://zl198751.iteye.com/blog/1848575

猜你喜欢

转载自blog.csdn.net/brycegao321/article/details/79099637