最近無意接觸了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
}
}
如上面源代碼所示,用嵌入的匯編實現的, CPU指令是 cmpxchg,程序會根據當前處理器的類型來決定是否為cmpxchg指令添加lock前綴。如果程序是在多處理器上運行,就為cmpxchg指令加上lock前綴(lock cmpxchg).反之,如果程序是在單處理器上運行,就省略lock前綴(單處理器自身會維護單處理器內的順序一致性,不需要lock前綴提供的內存屏障效果).lock前綴的作用說明:1禁止該指令與之前和之后的讀和寫指令重排序,2把寫緩沖區中的所有數據刷新到內存中。
總的來說,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
---------------------
原文:https://blog.csdn.net/u013404471/article/details/47297123
下面在看java並發編程藝術時有一段關於CAS的實現!
在Java中可以通過鎖和循環CAS的方式來實現原子操作。
(1)使用循環CAS實現原子操作
JVM中的CAS操作正是利用了處理器提供的CMPXCHG指令實現的。自旋CAS實現的基本思路就是循環進行CAS操作直到成功為止,以下代碼實現了一個基於CAS線程安全的計數器方法safeCount和一個非線程安全的計數器count。
從Java 1.5開始,JDK的並發包里提供了一些類來支持原子操作,如AtomicBoolean(用原子方式更新的boolean值)、AtomicInteger(用原子方式更新的int值)和AtomicLong(用原子方式更新的long值)。這些原子包裝類還提供了有用的工具方法,
比如以原子的方式將當前值自增1和自減1。
package com.odyun;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
public class CASTest {
private AtomicInteger atomicI = new AtomicInteger(0);
private int i = 0;
public static void main(String[] args) {
final CASTest cas = new CASTest();
List<Thread> ts = new ArrayList<Thread>(600);
long start = System.currentTimeMillis();
for (int j = 0; j < 100; j++) {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
cas.count();
cas.safeCount();
}
}
});
ts.add(t);
}
for (Thread t : ts) {
t.start();
}
// 等待所有線程執行完成
for (Thread t : ts) {
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(cas.i);
System.out.println(cas.atomicI.get());
System.out.println(System.currentTimeMillis() - start);
}
/** * 使用CAS實現線程安全計數器 */
private void safeCount() {
for (;;) {
int i = atomicI.get();
boolean suc = atomicI.compareAndSet(i, ++i);
if (suc) {
break;
}
}
}
/**
* 非線程安全計數器
*/
private void count() {
i++;
}
}
