典型物件池模型的“物件過早歸還”現象分析 (轉)

amyz發表於2007-08-15
典型物件池模型的“物件過早歸還”現象分析 (轉)[@more@]

常見的池實現方法雖然各不相同,但其卻大同小異,差別主要在於物件池的策略層上。《 Enterprise Design Patterns: Patterns in Java, Volume 3》中給出的物件池模型非常具有代表性。為敘述方便,簡稱為模型1,本文以此模型為物件進行討論。模型1如下圖:

:namespace prefix = o ns = "urn:schemas--com::office" />

ectratio="t" v:ext="edit">

圖1 物件池模型1

通常只有當物件可複用時,使用物件池才有意義。圖中Reusable是物件池所管理的可複用物件。應用中的任意(Component)都可以成為Client。圖中,Client透過ReusablePool.acquireReusable從物件池中取出物件,透過ReusablePool.releaseReusable將物件歸還給物件池;每個Reusable物件只能被一個Client 引用。Reusable與Client之間的關係是M:1,這個關係對模型1很重要。因為只有這樣才能保證Client能在任意時刻的 releaseReusable。

從物件池角度來看,Reusable物件Obj要麼在池中,要麼不在。而acquireReusable只能取出池中的物件,所以任意時刻只有一個Client能透過acquireReusable獲得Obj;而在Obj被歸還之前,任何其他Component都無法透過acquireReusable得到Obj。因此只要應用程式總是透過acquireReusable來獲得Reusable物件,就能保證Reusable與Client之間的M:1關係(而且模型1也僅將透過acquireReusable獲取Reusable物件的Component看作Client,當然這只是模型1的主觀意願)。但是由於缺乏機制,應用程式非常輕易的就能繞過acquireReusable而獲得對Obj的引用。例如首先語句ComponentA.obj = Reusable.acquireReusable()取出物件ObjA,並用ComponentA.obj引用ObjA;然後執行賦值操作ComponentB.obj=ComponentA.obj,使ComponentB.obj也引用ObjA。儘管模型1不認為ComponentB是ObjA的Client(因為模型1中ObjA只能有一個Client,此時就是ComponentA),但實際上Reusable與Client之間的M:1關係還是被破壞了。如果ComponentA忽視這個事實,一如既往的呼叫releaseReusable就會引起“物件過早歸還”問題 - 在應用程式的一部分還擁有對Reusable物件的引用時,應用程式的另外一部分將此物件歸還給物件池了。考慮一個具體的例子:

例1:假設在某個應用程式中ComponentA、ComponentB同時執行在兩個不同的執行緒A、B中,下面是此應用程式在操作的干預下的一次執行過程:

l  t0時刻,執行緒A執行ComponentA.obj = ReusablePool.acquireReusable(),取出物件ObjA並對它進行初始化;

l  t1時刻,執行緒A被中斷,執行緒B被排程,執行緒B執行操作ComponentB.obj=ComponentA.obj;

l  t2時刻,執行緒排程發生,執行緒A執行。ComponentA呼叫releaseReusable()歸還ObjA。此時就出現了ComponentB.obj引用了物件池中的物件ObjA的情況,這是很危險的;

l  t3時刻,執行緒再次切換,執行緒B執行;

l  t4時刻,ComponentB正準備操作ObjA,發生執行緒切換,執行緒A執行。ComponentA再次呼叫acquireReusable(),這次正巧又得到了ObjA,並再次初始化ObjA,設定其狀態;

l  t5時刻,執行緒B被排程,ComponentB未意識到ObjA的狀態已經被修改了,繼續操作ObjA。

不難發現,在t5時刻對ComponentB來說ObjA的狀態已經混亂了,可是ComponentB並未意識到這一點,繼續操作ObjA。這樣就出現了“物件過早歸還”問題。“物件過早歸還”問題對應用程式來說可能是災難性的。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752019/viewspace-958249/,如需轉載,請註明出處,否則將追究法律責任。

相關文章