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);
        }
    
        ...
    }
  • 相关阅读:
    C#中 @ 的用法
    ASP.NET页面间传值
    ASP.NET中常用的文件上传下载方法
    把图片转换为字符
    把图片转换为字符
    JavaScript 时间延迟
    Using WSDLs in UCM 11g like you did in 10g
    The Definitive Guide to Stellent Content Server Development
    解决RedHat AS5 RPM安装包依赖问题
    在64位Windows 7上安装Oracle UCM 10gR3
  • 原文地址:https://www.cnblogs.com/liuboyuan/p/10449503.html
Copyright © 2011-2022 走看看