接上一篇博客并发编程-多线程共享变量不安全,分析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锁
效率要高很多。