前言
最近關(guān)注著限流、降級相關(guān)的設(shè)計,開源的Hystrix提供了一種設(shè)計思路。限流降級的前提是需要了解系統(tǒng)的各種狀態(tài),服務(wù)的響應(yīng)情況,接口的調(diào)用情況,數(shù)據(jù)庫的情況等等。其中很重要的一個指標(biāo)就是qps,那么如何統(tǒng)計qps?Hystrix中有個設(shè)計非常好的類HystrixRollingNumber,非常適合用來統(tǒng)計qps。HystrixRollingNumber中利用了LongAdder來提高效率,所以本文先會介紹AtomicLong,UnSafe,下篇文章介紹LongAdder,下下篇文章介紹HystrixRollingNumber...
AtomicLong簡介
AtomicLong是一個可以原子更新的long值,主要方法有:
- get()
- set(long newValue)
- lazySet(long newValue)
- getAndSet(long newValue)
- boolean compareAndSet(long expect, long update)
- long getAndIncrement()
- long getAndAdd(long delta)
- long incrementAndGet()
- long decrmentAndGet()
- ...
其中最值得關(guān)注的方法是boolean compareAndSet(long expect, long update)以及l(fā)ong getAndAdd(long delta),前者代表著cas,是底層支持的一種原子更新的方法,cas可用于實(shí)現(xiàn)“樂觀鎖”等。后者的long getAndAdd(long delta)用于給舊值增加delta,并且返回舊值,整個方法是原子的,底層利用了cas。
AtomicLong實(shí)現(xiàn)
AtomicLong的compareAndSet、getAndAdd等是利用Unsafe的相關(guān)功能實(shí)現(xiàn)的,貼一段AtomicLong對于Unsafe的使用
// setup to use Unsafe.compareAndSwapLong for updates
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long valueOffset;
static {
try {
valueOffset = unsafe.objectFieldOffset
(AtomicLong.class.getDeclaredField("value"));
} catch (Exception ex) { throw new Error(ex); }
}
private volatile long value;
上述代碼中使用Unsafe.getUnsafe()取得了Unsafe對象,通過Unsafe對象的unsafe.objectFieldOffset方法取得了value值在AtomicLong對象中的內(nèi)存偏移地址(這一點(diǎn)學(xué)過c/c++的應(yīng)該很容易理解)。為了更好地理解AtomicLong實(shí)現(xiàn),下面要插入一段Unsafe的分析。
Unsafe為啥“unsafe”
Unsafe為啥叫“unsafe”,是因為它可以直接操作內(nèi)存地址,直接park/unpark線程,而且sun的每個版本的jdk中對于其實(shí)現(xiàn)都可能調(diào)整,直接使用非?!安话踩保唧w可以參考下這個問題下R大的回答。
Unsafe對象的獲取
AtomicLong中通過Unsafe.getUnsafe()獲取Unsafe對象,代碼如下:
public static Unsafe getUnsafe() {
Class var0 = Reflection.getCallerClass();
if(!VM.isSystemDomainLoader(var0.getClassLoader())) {
throw new SecurityException("Unsafe");
} else {
return theUnsafe;
}
從上可以看出會檢測調(diào)用getUnsafe的類對象的類是不是由BootstrapClassloader加載的,顯然我們自己定義的類沒法使用
getUnsafe拿到對象,而是會一直報SecurityException。但是我們還是另辟蹊徑:
Field field = Unsafe.class.getDeclaredField("theUnsafe");
field.setAccessible(true);
Unsafe unsafe = (Unsafe) field.get(null);
使用以上反射代碼可以成功拿到Unsafe中的static對象theUnsafe。
Unsafe中的native代碼
Unsafe中存在很多native的方法,主要涉及到直接分配或釋放內(nèi)存、直接獲取或者操作對象的字段值,掛起和恢復(fù)線程,cas等功能。幾種典型的native方法如下:
public native long allocateMemory(long var1);
public native void setMemory(Object var1, long var2, long var4, byte var6);
public native void putChar(long var1, char var3);
public native char getChar(long var1);
public native long getLongVolatile(Object var1, long var2);
public native boolean compareAndSwapLong(Object var1, long var2, long var4, long var6);
...
native方法的實(shí)現(xiàn)在c++,java通過JNI的方式調(diào)用,從網(wǎng)上找到了c++的實(shí)現(xiàn)代碼,我們主要看看其中的getLongVolatile和getLong以及cas相關(guān)代碼的實(shí)現(xiàn),代碼可以從這里看到
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;
}
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;
}
學(xué)過c++對于上述代碼應(yīng)該非常容易看懂,基本都是基于指針操作內(nèi)存。
Unsafe中的非native代碼
Unsafe中的非native代碼主要包括了getAndSetXXX系列和getAndAddXXX系列,讓我看看它的源碼
public final long getAndAddLong(Object var1, long var2, long var4) {
long var6;
do {
var6 = this.getLongVolatile(var1, var2);
} while(!this.compareAndSwapLong(var1, var2, var6, var6 + var4));
return var6;
}
public final long getAndSetLong(Object var1, long var2, long var4) {
long var6;
do {
var6 = this.getLongVolatile(var1, var2);
} while(!this.compareAndSwapLong(var1, var2, var6, var4));
return var6;
}
基本就是循環(huán)+cas的處理方式,這種方式在線程競爭不是很大的情況下還是非常好用的
AtomicLong對Unsafe的使用
定位對象中字段內(nèi)存偏移量
直接內(nèi)存操作首要的需要得到對象指針以及偏移地址
static {
try {
valueOffset = unsafe.objectFieldOffset
(AtomicLong.class.getDeclaredField("value"));
} catch (Exception ex) { throw new Error(ex); }
}
private static final long valueOffset;
getAndSetXXX以及getAndIncrement
在知道unsafe的處理方式后,AtomicLong的代碼就很簡單了
public final long getAndIncrement() {
return unsafe.getAndAddLong(this, valueOffset, 1L);
}
public final long getAndSet(long newValue) {
return unsafe.getAndSetLong(this, valueOffset, newValue);
}
public final boolean compareAndSet(long expect, long update) {
return unsafe.compareAndSwapLong(this, valueOffset, expect, update);
}
總結(jié)
本文簡單分析了AtomicLong以及Unsafe的使用,需要注意的是AtomicLong的所有單操作都是原子操作。下文將分析LongAdder并與AtomicLong進(jìn)行相應(yīng)的對比。