• 并发编程-Atomic的compareAndSet


    接上一篇博客并发编程-多线程共享变量不安全,分析Atomic原子类是怎么保证线程安全的。

    并发三个基本概念:

    1.原子性:操作是线程私有的,不能拆分成多个步骤,被其他线程影响;(官方版:操作不可中断,全部执行或全部失败)

    2.可见性:对共享变量的修改能够被其他线程可见;

    3.有序性:cpu会为了优化对指令进行重排序,有序性保证线程内指令顺序一致。

    看AtomicInteger的incrementAndGet方法如何保证原子性的。以下代码是CAS的过程:

    public final int incrementAndGet() {
            for (;;) {
                int current = get(); //从主内存中拷贝value放到本地线程栈内存中 这二行代码可能另一个线程也在执行
                int next = current + 1; //做增加操作
                if (compareAndSet(current, next)) 
                    return next;
            }
        }
    //调用unsafe的compareAndSwapInt方法
    public final boolean compareAndSet(int expect, int update) {
            return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
        }
    
    public final int get() {
            return value;
        }
    
    private volatile int value;  //volatile保证了可见性,在incrementAndGet()方法中get()方法获取的value是从主内存中拿到的
    private static final Unsafe unsafe = Unsafe.getUnsafe(); 

    private static final long valueOffset;
    static {
    try {
    valueOffset
    = unsafe.objectFieldOffset (AtomicInteger.class.getDeclaredField("value")); //获取value值在AtomicInteger内存地址中的偏移量
            }
    catch (Exception ex)
    { throw new Error(ex); }
    }

    //Unsafe class 代码 public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);

    原理就是:线程A从主存中拿到value值,对value值做+1操作赋给newValue,然后再获取一次主内存的值,比较value和原值是否相等,如果相等,证明这个值没有被别的线程改变,对它设置新的值,如果不相等,证明这个值被别的线程更改了,重新获取一次,再次比较,直到相等、设置新值,返回新值。

    再来看一下compareAndSwapInt方法 unsafe.cpp,(http://hg.openjdk.java.net/jdk7/jdk7/hotspot/file/9b0ca45cd756/src/share/vm/prims/unsafe.cpp):

    UNSAFE_ENTRY(jboolean, Unsafe_CompareAndSwapInt(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jint e, jint x))
      UnsafeWrapper("Unsafe_CompareAndSwapInt");
      oop p = JNIHandles::resolve(obj);
      jint* addr = (jint *) index_oop_from_field_offset_long(p, offset);
      return (jint)(Atomic::cmpxchg(x, addr, e)) == e;
    UNSAFE_END

    主要实现Atomic::cmpxchg(x, addr, e)  继续看Atomic::cmpxchg , 这个本地方法的最终实现在openjdk的如下位置:

    openjdk-7-fcs-src-b147-27jun2011openjdkhotspotsrcoscpuwindowsx86vmatomicwindowsx86.inline.hpp
    (对应于windows操作系统,X86处理器)
    // Adding a lock prefix to an instruction on MP machine
    // VC++ doesn't like the lock prefix to be on a single line
    // so we can't insert a label after the lock prefix.
    // By emitting a lock prefix, we can define a label after it.
    #define LOCK_IF_MP(mp) __asm cmp mp, 0  
                           __asm je L0      
                           __asm _emit 0xF0 
                           __asm L0:
     
    inline jint     Atomic::cmpxchg    (jint     exchange_value, volatile jint*     dest, jint     compare_value) {
      // alternative for InterlockedCompareExchange
      int mp = os::is_MP();  //判断当前系统环境
      __asm {
        mov edx, dest   
        mov ecx, exchange_value
        mov eax, compare_value
        LOCK_IF_MP(mp)
        cmpxchg dword ptr [edx], ecx
      }
    }
    LOCK_IF_MP指的是如果是在多核环境时,cpu会加上lock,如果是单核环境,不会加lock,因此实际上也是通过lock在cpu层面加上了屏障,排他锁。但是cpu层面的锁比jvm层的sync锁
    效率要高很多。
     
    欢迎关注Java流水账公众号
  • 相关阅读:
    mysql触发器的实战经验
    mysql存储程序查看
    索引性能优化(待整理)
    MySQL性能测试工具
    列级触发器 SQL Server
    synthetic division
    This function has none of DETERMINISTIC, NO SQL, or READS SQL DATA in its de
    mysql触发器和定时器
    数据库触发器有以下的作用
    mysql 日志
  • 原文地址:https://www.cnblogs.com/guofu-angela/p/9363654.html
Copyright © 2020-2023  润新知