我們知道,實現了ReferenceCounted接口的類的對象都會在引用計數的作用下進行顯式的回收。當引用計數爲0時,這個對象就不能再被訪問了。而這個接口提供了兩個方法給我們來操作引用計數。
- retain()
- release()
而這個操作是必須保證是在多線程的情況下是安全的,所以他們的操作都是原子的。以retain爲例
private ReferenceCounted retain0(int increment) {
int oldRef = refCntUpdater.getAndAdd(this, increment);//使用更新器增加引用計數值,並返回舊值
if (oldRef <= 0 || oldRef + increment < oldRef) {//如果舊值爲0,則恢復這個對象的引用計數值並拋出異常
// Ensure we don't resurrect (which means the refCnt was 0) and also that we encountered an overflow.
refCntUpdater.getAndAdd(this, -increment);
throw new IllegalReferenceCountException(oldRef, increment);
}
return this;
}
所以關鍵就在這個refCntUpdater上。我們看一下他的聲明
private static final AtomicIntegerFieldUpdater<AbstractReferenceCounted> refCntUpdater =
AtomicIntegerFieldUpdater.newUpdater(AbstractReferenceCounted.class, "refCnt");
他是一個靜態,所以是被這個類所有的對象共享的。這個對象是AtomicIntegerFieldUpdater類通過靜態方法創建的。我們看一下這個類。
public abstract class AtomicIntegerFieldUpdater<T>
{
@CallerSensitive//這個方法有兩個參數,一個是需要進行原子操作類,另一個是需要進行原子操作的這個類的字段的名稱
public static <U> AtomicIntegerFieldUpdater<U> newUpdater(Class<U> paramClass, String paramString)
{
return new AtomicIntegerFieldUpdaterImpl(paramClass, paramString, Reflection.getCallerClass());
}
我們查看一下在retain0方法中調用的原子操作方法getAndAdd方法
public int getAndAdd(T paramT, int paramInt)
{
int i;
int j;
do
{
i = get(paramT);
j = i + paramInt;
} while (!compareAndSet(paramT, i, j));
return i;
}
其中採用的方法爲自詢鎖的方法,反覆進行操作,如果操作失敗,則再次循環,直至成功更新。
compareAndSet方法的實現是一種native方法,不作討論。他的作用就是再在多線程的環境下,如果兩個線程同時操作了這個對象,並且同時進入到了這個方法當中,那麼就對比這個對象中更新器指定的字段的舊值跟實際上這個對象該值(因爲可能會發生在獲取這個屬性值之後,另一個線程對其進行了修改),如果判定這兩個值相等,則表示沒有線程在這一瞬間對這個字段進行了更新,則compareAndSet更新成功。如果值判定不相等,則表示有線程在這一瞬間執行了更新操作,需要再次回到循環中進行。簡稱cas操作。
而AtomicIntegerFieldUpdater的cas操作對需要進行原子操作的屬性是有要求的,
- 必須保證這個屬性是int類型,而不是Integer類型,如果是Integer,則使用AtomicReferenceFieldUpdater。
- 必須保證這個屬性是被volatile修飾符修飾(如果不是,那麼一個線程對其的修改不會立刻更新到內存當中,所以對其他線程不可見),volatile的作用就是保證了其在多線程環境下的可見性。
- 必須保證這個屬性不是static.
- 必須保證這個屬性是可以在外部被訪問到的,因爲AtomicIntegerFieldUpdater肯定需要通過反射的方式獲取指定的屬性的值。