Java并发学习笔记10 CAS

bilibili-Java并发学习笔记10 CAS

基于 java 1.8.0

P39_CAS详解及透过字节码分析变量操作的原子性

  • 悲观锁
    • synchronized 关键字与 Lock 等锁机制都是悲观锁:无论做任何操作,首先都需要先上锁,接下来再去执行后续操作,从而确保了接下来的所有操作都是由当前这个线程来执行的。
  • 乐观锁
    • 线程在操作之前不会做任何预先的处理,而是直接去执行;当在最后执行变量更新的时候,当前线程需要有一种机制来确保当前被操作的变量是没有被其他线程修改的;CAS乐观锁的一种非常重要的实现方式
    • CAS (Compare And Swap) 比较与交换 : 这是一个不断循环的过程,一直到变量值被修改成功为止。CAS 本身是由硬件指令来提供支持的,换句话说,硬件是通过一个原子指令来实现比较与交换的;因此,CAS 可以确保变量操作的原子性的。
      • hotspot 源码实现中调用 cmpxchg 函数(hotspot/src/share/vm/prims/unsafe.cpp)
      • 其调用了汇编指令 cmpxchg (hotspot/src/share/vm/runtime/Atomic.cpp)
      • 在IA64,x86 指令集中有 cmpxchg 指令完成 CAS 功能,在 sparc-TSO 也有 casa 指令实现,而在 ARM 和 PowerPC 架构下,则需要使用一对 ldrex/strex 指令来完成 LL/SC 的功能。在精简指令集的体系架构中,则通常是靠一对儿指令,如:load and reserve 和 store conditional 实现的,在大多数处理器上 CAS 都是个非常轻量级的操作。(https://juejin.im/post/5de5f8575188253232282e8e)
    • Atomic 原子类其底层基于 CAS 乐观锁来实现
    • LongAdder 其底层也是基于 CAS 机制实现的

package new_package.thread.p39;

public class CasTest1 {

    private int count;

    public int getCount() {
        return count;
    }

    public void increaseCount() {
        // java 代码的一句
        ++this.count;
    }
}

javap -v xxx

// 字节码四行指令
0: aload_0
1: dup
2: getfield      #2                  // Field count:I  获取值
5: iconst_1                                            1
6: iadd                                                计算 +1
7: putfield      #2                  // Field count:I  赋新值
10: return

读取 -> 修改 -> 写入 : 不是原子操作


加锁方式改进:

package new_package.thread.p39;

public class CasTest1 {


    private int count;

    public synchronized int getCount() {
        return count;
    }

    public synchronized void increaseCount() {
        ++this.count;
    }
}

错误改进:

package new_package.thread.p39;

public class CasTest1 {

    // volatile 不保证原子性
    // 只保证可见性和防止指令重排序
    private volatile int count;

    public int getCount() {
        return count;
    }

    public synchronized void increaseCount() {
        ++this.count;
    }
}

正确改进:

package new_package.thread.p39;

import java.util.concurrent.atomic.AtomicInteger;

public class CasTest2 {

    private AtomicInteger count = new AtomicInteger(0);

    public int getCount() {
        return count.get();
    }

    public void increaseCount() {
        count.incrementAndGet();
    }
}
package new_package.thread.p39;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.LongAdder;
import java.util.stream.IntStream;

public class CasTest3 {

    private LongAdder count = new LongAdder();

    public int getCount() {
        return count.intValue();
    }

    public void increaseCount() {
        count.increment();
    }

    public static void main(String[] args) throws InterruptedException {
        CasTest3 casTest3 = new CasTest3();
        CountDownLatch countDownLatch = new CountDownLatch(10);
        IntStream.range(0, 10).forEach(n -> new Thread(() -> {
            for (int i = 0; i < 100; i++) {
                casTest3.increaseCount();
            }
            countDownLatch.countDown();
        }).start());
        countDownLatch.await();
        System.out.println(casTest3.getCount());
    }
}

P40_CAS底层实现与源码剖析

对于 CAS 来说,其操作步骤主要涉及到三步:

    1. 需要被操作的内存值 V
    1. 需要进行比较的值 A
    1. 需要进行写入的值 B

只有当 V==A 时,CAS 才会通过原子操作的手段来将 V 的值更新为 B ;


CAS 存在的问题:

    1. 循环开销问题:并发量大的情况下会导致线程一直自旋;
    1. 只能保证一个变量的原子操作:可以通过 AtomicReference 来实现多个变量的原子操作;
    1. ABA 问题:加版本号;

package java.util.concurrent.atomic;
import java.util.function.IntUnaryOperator;
import java.util.function.IntBinaryOperator;
import sun.misc.Unsafe;

/**
 * @since 1.5
 * @author Doug Lea
*/
public class AtomicInteger extends Number implements java.io.Serializable {
    private static final long serialVersionUID = 6214790243416807050L;

    // 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); }
    }

    private volatile int value;

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

    public AtomicInteger() {
    }

    public final int get() {
        return value;
    }

   public final int getAndSet(int newValue) {
        return unsafe.getAndSetInt(this, valueOffset, newValue);
    }

    public final int getAndAdd(int delta) {
        return unsafe.getAndAddInt(this, valueOffset, delta);
    }

    public final int incrementAndGet() {
        return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
    }

    // ...
}
package sun.misc;
import java.security.*;
import java.lang.reflect.*;

import sun.reflect.CallerSensitive;
import sun.reflect.Reflection;


/**
 * A collection of methods for performing low-level, unsafe operations.
 * Although the class and all methods are public, use of this class is
 * limited because only trusted code can obtain instances of it.
 *
 * @author John R. Rose
 * @see #getUnsafe
 */
public final class Unsafe {

    private static native void registerNatives();
    static {
        registerNatives();
        sun.reflect.Reflection.registerMethodsToFilter(Unsafe.class, "getUnsafe");
    }

    private Unsafe() {}

    private static final Unsafe theUnsafe = new Unsafe();

    @CallerSensitive
    public static Unsafe getUnsafe() {
        Class<?> caller = Reflection.getCallerClass();
        if (!VM.isSystemDomainLoader(caller.getClassLoader()))
            throw new SecurityException("Unsafe");
        return theUnsafe;
    }

    public final int getAndSetInt(Object o, long offset, int newValue) {
        int v;
        do {
            v = getIntVolatile(o, offset);
        } while (!compareAndSwapInt(o, offset, v, newValue));
        return v;
    }

    public final native boolean compareAndSwapInt(Object o, long offset,
                                                  int expected,
                                                  int x);

    // ...

}
// hotspot/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

// hotspot/src/share/vm/runtime/atomic.cpp
jbyte Atomic::cmpxchg(jbyte exchange_value, volatile jbyte* dest, jbyte compare_value) {
  assert(sizeof(jbyte) == 1, "assumption.");
  uintptr_t dest_addr = (uintptr_t)dest;
  uintptr_t offset = dest_addr % sizeof(jint);
  volatile jint* dest_int = (volatile jint*)(dest_addr - offset);
  jint cur = *dest_int;
  jbyte* cur_as_bytes = (jbyte*)(&cur);
  jint new_val = cur;
  jbyte* new_val_as_bytes = (jbyte*)(&new_val);
  new_val_as_bytes[offset] = exchange_value;
  while (cur_as_bytes[offset] == compare_value) {
    jint res = cmpxchg(new_val, dest_int, cur);
    if (res == cur) break;
    cur = res;
    new_val = cur;
    new_val_as_bytes[offset] = exchange_value;
  }
  return cur_as_bytes[offset];
}

package new_package.thread.p39;

import sun.misc.Unsafe;

import java.util.concurrent.atomic.AtomicInteger;

public class CasTest4 {
    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);
        }
    }

    private volatile int value;

    public static void main(String[] args) {
        System.out.println("hello");
    }
}
/**
 * Exception in thread "main" java.lang.ExceptionInInitializerError
 * Caused by: java.lang.SecurityException: Unsafe
 * at sun.misc.Unsafe.getUnsafe(Unsafe.java:90)
 * at new_package.thread.p39.CasTest4.<clinit>(CasTest4.java:13)
 */

可以通过 Java 反射来获取 unsafe 实例;

package new_package.thread.p39;

import sun.misc.Unsafe;

import java.lang.reflect.Field;
import java.security.AccessController;
import java.security.PrivilegedExceptionAction;
import java.util.concurrent.atomic.AtomicInteger;

public class CasTest5 {
    private static final Unsafe unsafe;
    private volatile int value;
    private static final long valueOffset;

    static {
        try {
            final PrivilegedExceptionAction<Unsafe> action = new PrivilegedExceptionAction<Unsafe>() {
                @Override
                public Unsafe run() throws Exception {
                    Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
                    theUnsafe.setAccessible(true);
                    return (Unsafe) theUnsafe.get(null);
                }
            };
            unsafe = AccessController.doPrivileged(action);
        } catch (Exception e) {
            throw new RuntimeException("Unable to load unsafe", e);
        }

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

    public static void main(String[] args) {
        System.out.println("hello");
    }

}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章