JVM volatile语义:
mark:
(1)Java 内存模型不会对valatile指令的操作进行重排序:这个保证对volatile变量的操作时按照指令的出现顺序执行的。
(2)volatile变量不会被缓存在寄存器中(只有拥有线程可见)或者其他对CPU不可见的地方,每次总是从主存中读取volatile变量的结果。也就是说对于volatile变量的修改,其它线程总是可见的,并且不是使用自己线程栈内部的变量。也就是在happens-before法则中,对一个valatile变量的写操作后,其后的任何读操作理解可见此写操作的结果。
Volatile变量的读写
读:置本地数据无效,总是从主存中读取数据
写:更新本地数据,然后刷新主存中的数据
CAS操作:利用CPU中的特殊指令cmpxchg (intel)
CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。
原子变量的实现:
首先变量是volatile的保证可见性;
对变量的更新在一个循环中实现,例如:
for (;;) {
int current = get();
int next = current + 1;
if (compareAndSet(current, next))
return next;
}
最少执行一次方法就可以返回,否则一直执行;
CAS的ABA问题
比如说一个线程one从内存位置V中取出A,这时候另一个线程two也从内存中取出A,并且two进行了一些操作变成了B,然后two又将V位置的数据变成A,这时候线程one进行CAS操作发现内存中仍然是A,然后one操作成功。尽管线程one的CAS操作成功,但是不代表这个过程就是没有问题的。如果链表的头在变化了两次后恢复了原值,但是不代表链表就没有变化。因此前面提到的原子操作AtomicStampedReference/AtomicMarkableReference就很有
用了。这允许一对变化的元素进行原子操作。
Java通过Unsafe类通过JNI结合CPU的CAS指令来完成
sun.misc.natUnsafe.cc 的源码:
#include <gcj/cni.h> #include <gcj/field.h> #include <gcj/javaprims.h> #include <jvm.h> #include <sun/misc/Unsafe.h> #include <java/lang/System.h> #include <java/lang/InterruptedException.h> #include <java/lang/Thread.h> #include <java/lang/Long.h> #include "sysdep/locks.h" // Use a spinlock for multi-word accesses class spinlock { static volatile obj_addr_t lock; public: spinlock () { while (! compare_and_swap (&lock, 0, 1)) _Jv_ThreadYield (); } ~spinlock () { release_set (&lock, 0); } }; // This is a single lock that is used for all synchronized accesses if // the compiler can't generate inline compare-and-swap operations. In // most cases it'll never be used, but the i386 needs it for 64-bit // locked accesses and so does PPC32. It's worth building libgcj with // target=i486 (or above) to get the inlines. volatile obj_addr_t spinlock::lock; static inline bool compareAndSwap (volatile jint *addr, jint old, jint new_val) { jboolean result = false; spinlock lock; if ((result = (*addr == old))) *addr = new_val; return result; } static inline bool compareAndSwap (volatile jlong *addr, jlong old, jlong new_val) { jboolean result = false; spinlock lock; if ((result = (*addr == old))) *addr = new_val; return result; } static inline bool compareAndSwap (volatile jobject *addr, jobject old, jobject new_val) { jboolean result = false; spinlock lock; if ((result = (*addr == old))) *addr = new_val; return result; } jlong sun::misc::Unsafe::objectFieldOffset (::java::lang::reflect::Field *field) { _Jv_Field *fld = _Jv_FromReflectedField (field); // FIXME: what if it is not an instance field? return fld->getOffset(); } jint sun::misc::Unsafe::arrayBaseOffset (jclass arrayClass) { // FIXME: assert that arrayClass is array. jclass eltClass = arrayClass->getComponentType(); return (jint)(jlong) _Jv_GetArrayElementFromElementType (NULL, eltClass); } jint sun::misc::Unsafe::arrayIndexScale (jclass arrayClass) { // FIXME: assert that arrayClass is array. jclass eltClass = arrayClass->getComponentType(); if (eltClass->isPrimitive()) return eltClass->size(); return sizeof (void *); } // These methods are used when the compiler fails to generate inline // versions of the compare-and-swap primitives. jboolean sun::misc::Unsafe::compareAndSwapInt (jobject obj, jlong offset, jint expect, jint update) { jint *addr = (jint *)((char *)obj + offset); return compareAndSwap (addr, expect, update); } jboolean sun::misc::Unsafe::compareAndSwapLong (jobject obj, jlong offset, jlong expect, jlong update) { volatile jlong *addr = (jlong*)((char *) obj + offset); return compareAndSwap (addr, expect, update); } jboolean sun::misc::Unsafe::compareAndSwapObject (jobject obj, jlong offset, jobject expect, jobject update) { jobject *addr = (jobject*)((char *) obj + offset); return compareAndSwap (addr, expect, update); } void sun::misc::Unsafe::putOrderedInt (jobject obj, jlong offset, jint value) { volatile jint *addr = (jint *) ((char *) obj + offset); *addr = value; } void sun::misc::Unsafe::putOrderedLong (jobject obj, jlong offset, jlong value) { volatile jlong *addr = (jlong *) ((char *) obj + offset); spinlock lock; *addr = value; } void sun::misc::Unsafe::putOrderedObject (jobject obj, jlong offset, jobject value) { volatile jobject *addr = (jobject *) ((char *) obj + offset); *addr = value; } void sun::misc::Unsafe::putIntVolatile (jobject obj, jlong offset, jint value) { write_barrier (); volatile jint *addr = (jint *) ((char *) obj + offset); *addr = value; } void sun::misc::Unsafe::putLongVolatile (jobject obj, jlong offset, jlong value) { volatile jlong *addr = (jlong *) ((char *) obj + offset); spinlock lock; *addr = value; } void sun::misc::Unsafe::putObjectVolatile (jobject obj, jlong offset, jobject value) { write_barrier (); volatile jobject *addr = (jobject *) ((char *) obj + offset); *addr = value; } #if 0 // FIXME void sun::misc::Unsafe::putInt (jobject obj, jlong offset, jint value) { jint *addr = (jint *) ((char *) obj + offset); *addr = value; } #endif void sun::misc::Unsafe::putLong (jobject obj, jlong offset, jlong value) { jlong *addr = (jlong *) ((char *) obj + offset); spinlock lock; *addr = value; } void sun::misc::Unsafe::putObject (jobject obj, jlong offset, jobject value) { jobject *addr = (jobject *) ((char *) obj + offset); *addr = value; } jint sun::misc::Unsafe::getIntVolatile (jobject obj, jlong offset) { volatile jint *addr = (jint *) ((char *) obj + offset); jint result = *addr; read_barrier (); return result; } jobject sun::misc::Unsafe::getObjectVolatile (jobject obj, jlong offset) { volatile jobject *addr = (jobject *) ((char *) obj + offset); jobject result = *addr; read_barrier (); return result; } jlong sun::misc::Unsafe::getLong (jobject obj, jlong offset) { jlong *addr = (jlong *) ((char *) obj + offset); spinlock lock; return *addr; } jlong sun::misc::Unsafe::getLongVolatile (jobject obj, jlong offset) { volatile jlong *addr = (jlong *) ((char *) obj + offset); spinlock lock; return *addr; } void sun::misc::Unsafe::unpark (::java::lang::Thread *thread) { natThread *nt = (natThread *) thread->data; nt->park_helper.unpark (); } void sun::misc::Unsafe::park (jboolean isAbsolute, jlong time) { using namespace ::java::lang; Thread *thread = Thread::currentThread(); natThread *nt = (natThread *) thread->data; nt->park_helper.park (isAbsolute, time); }