zoukankan      html  css  js  c++  java
  • java多线程-cas及atomic

    大纲:

    1. cas
    2. atomic

    一、cas

    cas:compareAndSwap,一种乐观锁。

    cas思想:cas需要三个值,v是内存值,e是期望值,n是要修改的值。当内存中的值v等于预期值e(说明内存中的值没有被其他线程修改)、才可以修改v为n。

    cas伪代码

    boolean cas(int v,int e,int n){
    if(v==e){
    v=n;
    return true;
    }else {
    return false;
    }
    }

    cas是unsafe类中的native方法,是原子操作,保证线程安全。

    二、atomic

    在java.util.concurrent.atomic包中,提供了许多拥有原子操作的类,供并发编程使用。这个包中的类大都使用了cas操作来保证操作的原子性。

    以AtomicInteger为例,看下如何使用cas实现原子操作,这里为java1.8以下的源码:

    public class AtomicInteger extends Number implements java.io.Serializable {
    
        private volatile int value; //volatile保证可见性,一个线程更新后,其他线程立即可见
    
        public final int get() {
            return value;
        }
    
        public AtomicInteger(int initialValue) {
            value = initialValue;
        }
    
        public final int getAndIncrement() {
            for (;;) {
                int current = get();//预期值e
                int next = current + 1;//要修改的值n
                if (compareAndSet(current, next))
                    return current;
            }
            //这个相当于线程安全的a++操作,其思想就是不断进行cas操作,直至成功
        }
    
        public final boolean compareAndSet(int expect, int update) {
            return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
            //给bootStrap类加载器用的unsafe中全部是native方法。
            // valueOffset为变量在内存中偏移地址,用来找内存中的值v
        }
    
        ......
    }

     ABA问题:一个线程在cas操作时,期望值是A,另一个线程修改了期望值为B,就会导致cas操作失败。但是如果这时候又有一个线程再次修改期望值为A,cas操作将成功。

    如何解决:atomic包下AtomicStampedReference

    public class AtomicStampedReference<V> {
    
        private static class Pair<T> {
            final T reference;
            final int stamp;
            private Pair(T reference, int stamp) {
                this.reference = reference;
                this.stamp = stamp;
            }
            static <T> Pair<T> of(T reference, int stamp) {
                return new Pair<T>(reference, stamp);
            }
        }
    
        private volatile Pair<V> pair;
    
        public AtomicStampedReference(V initialRef, int initialStamp) {
            pair = Pair.of(initialRef, initialStamp);
            //AtomicStampedReference内部有一个静态内部类pair,pair不仅封装了数据还封装了一个stamp用于区分每次修改
        }
    
        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))); //cas比较pair对象(不仅比较内存值如期望值,同时比较期望stamp与内存stamp)
        }
    
        private boolean casPair(Pair<V> cmp, Pair<V> val) {
            return UNSAFE.compareAndSwapObject(this, pairOffset, cmp, val);
        }
    
        ...
    }
  • 相关阅读:
    Solr 配置连接数据库
    最大利润
    分割金条的最小代价
    民居点亮
    一个会议室最多安排几场宣讲
    N皇后问题
    Integer的缓存机制
    Windows快捷键
    二叉树中两个节点的最低公共祖节点
    判断二叉树是不是完全二叉树
  • 原文地址:https://www.cnblogs.com/liuboyuan/p/10449503.html
Copyright © 2011-2022 走看看