最近無意接觸了AtomicInteger類compareAndSet(從JDK5開始),搜了搜相關資料,整理了一下
首先要說一下,AtomicInteger類compareAndSet通過原子操作實現了CAS操作,最底層基於彙編語言實現。
簡單說一下原子操作的概念,“原子”代表最小的單位,所以原子操作可以看做最小的執行單位,該操作在執行完畢前不會被任何其他任務或事件打斷。
CAS是Compare And Set的一個簡稱,如下理解:
1,已知當前內存裏面的值current和預期要修改成的值new傳入
2,內存中AtomicInteger對象地址對應的真實值(因爲有可能別修改)real與current對比,
相等表示real未被修改過,是“安全”的,將new賦給real結束然後返回;不相等說明real已經被修改,結束並重新執行1直到修改成功
CAS相比Synchronized,避免了鎖的使用,總體性能比Synchronized高很多.
compareAndSet典型使用爲計數,如i++,++i,這裏以i++爲例:
- /**
- * Atomically increments by one the current value.
- *
- * @return the updated value
- */
- public final int incrementAndGet() {
- for (;;) {
- //獲取當前值
- int current = get();
- //設置期望值
- int next = current + 1;
- //調用Native方法compareAndSet,執行CAS操作
- if (compareAndSet(current, next))
- //成功後纔會返回期望值,否則無線循環
- return next;
- }
- }
compareAndSet方法實現:
JDK文檔對該方法的說明如下:如果當前狀態值等於預期值,則以原子方式將同步狀態設置爲給定的更新值。
- public final boolean compareAndSet(int expect, int update) {
- return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
- }
這裏解釋一下valueOffset變量,首先valueOffset的初始化在static靜態代碼塊裏面,代表相對起始內存地址的字節相對偏移量:
- private static final long valueOffset;
- static {
- try {
- valueOffset = unsafe.objectFieldOffset
- (AtomicInteger.class.getDeclaredField("value"));
- } catch (Exception ex) { throw new Error(ex); }
- }
在生成一個AtomicInteger對象後,可以看做生成了一段內存,對象中各個字段按一定順序放在這段內存中,字段可能不是連續放置的,
unsafe.objectFieldOffset(Field f)這個方法準確地告訴我"value"字段相對於AtomicInteger對象的起始內存地址的字節相對偏移量。
- private volatile int value;
- /**
- * Creates a new AtomicInteger with the given initial value.
- *
- * @param initialValue the initial value
- */
- public AtomicInteger(int initialValue) {
- value = initialValue;
- }
- /**
- * Creates a new AtomicInteger with initial value {@code 0}.
- */
- public AtomicInteger() {
- }
value是一個volatile變量,不同線程對這個變量進行操作時具有可見性,修改與寫入操作都會存入主存中,並通知其他cpu中該變量緩存行無效,保證了每次讀取都是最新的值
找到sun.misc.Unsafe.java:
- /**
- * Atomically update Java variable to <tt>x</tt> if it is currently
- * holding <tt>expected</tt>.
- * @return <tt>true</tt> if successful
- */
- public final native boolean compareAndSwapInt(Object o, long offset,int expected,int x);
繼續查找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 , 這個本地方法的最終實現在openjdk的如下位置:openjdk-7-fcs-src-b147-27jun2011\openjdk\hotspot\src\oscpu\windowsx86\vm\ atomicwindowsx86.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
- }
- }
總的來說,Atomic實現了高效無鎖(底層還是用到排它鎖,不過底層處理比java層處理要快很多)與線程安全(volatile變量特性),CAS一般適用於計數;多線程編程也適用,多個線程執行AtomicXXX類下面的方法,當某個線程執行的時候具有排他性,在執行方法中不會被打斷,直至當前線程完成纔會執行其他的線程。
參考文章:http://www.infoq.com/cn/articles/java-memory-model-5
http://hllvm.group.iteye.com/group/topic/37940
http://www.cnblogs.com/dolphin0520/p/3920373.html
轉自:http://blog.csdn.net/u013404471/article/details/47297123