日本搞逼视频_黄色一级片免费在线观看_色99久久_性明星video另类hd_欧美77_综合在线视频

國內最全IT社區平臺 聯系我們 | 收藏本站
阿里云優惠2
您當前位置:首頁 > php開源 > 綜合技術 > java/android 設計模式學習筆記(5)---對象池模式

java/android 設計模式學習筆記(5)---對象池模式

來源:程序員人生   發布時間:2016-06-15 14:12:10 閱讀次數:2561次

  這次要介紹1下對象池模式(Object Pool Pattern),這個模式為常見 23 種設計模式以外的設計模式,介紹的初衷主要是在平時的 android 開發中常常會看到,比如 ThreadPool 和 MessagePool 等。
  在 java 中,所有對象的內存由虛擬機管理,所以在某些情況下,需要頻繁創建1些生命周期很短使用完以后就能夠立即燒毀,但是數量很大的對象集合,那末此時 GC 的次數必定會增加,這時候候為了減小系統 GC 的壓力,對象池模式就很適用了。對象池模式也是創建型模式之1,它不是根據使用動態的分配和燒毀內存,而是保護1個已初始化好若干對象的對象池以供使用。客戶端使用的時候從對象池中去申請1個對象,當該對象使用完以后,客戶端會返回給對象池,而不是立即燒毀它,這步操作可以手動或自動完成。
  從 Java 語言的特性來分析1下,在 Java 中,對象的生命周期大致包括3個階段:對象的創建,對象的使用,對象的清除。因此,對象的生命周期長度可用以下的表達式表示:T = T1 + T2 +T3。其中T1表示對象的創建時間,T2 表示對象的使用時間,而 T3 則表示其清除時間。由此,我們可以看出,只有 T2 是真正有效的時間,而 T1、T3 則是對象本身的開消。下面再看看 T1、T3 在對象的全部生命周期中所占的比例。Java對象是通過構造函數來創建的,在這1進程中,該構造函數鏈中的所有構造函數也都會被自動調用。另外,默許情況下,調用類的構造函數時,Java 會把變量初始化成肯定的值:所有的對象被設置成 null,整數變量(byte、short、int、long)設置成 0, float 和 double 變量設置成 0.0,邏輯值設置成false。所以用new關鍵字來新建1個對象的時間開消是很大的,以下表所示:

運算操作 示例 標準化時間
本地賦值 i = n 1.0
實例賦值 this.i = n 1.2
方法調用 Funct() 5.9
新建對象 New Object() 980
新建數組 New int[10] 3100

從表中可以看出,新建1個對象需要980個單位的時間,是本地賦值時間的980倍,是方法調用時間的166倍,而若新建1個數組所花費的時間就更多了。
  再看清除對象的進程,我們知道,Java 語言的1個優勢,就是 Java 程序員勿需再像 C/C++ 程序員那樣,顯式地釋放對象,而由稱為垃圾搜集器(Garbage Collector)的自動內存管理系統,定時或在內存凸現出不足時,自動回收垃圾對象所占的內存。凡事有益總也有弊,這雖然為 Java 程序設計者提供了極大的方便,但同時它也帶來了較大的性能開消。這類開消包括兩方面,首先是對象管理開消,GC為了能夠正確釋放對象,它必須監控每個對象的運行狀態,包括對象的申請、援用、被援用、賦值等。其次,在 GC 開始回收“垃圾”對象時,系統會暫停利用程序的履行,而獨自占用 CPU。
  因此,如果要改良利用程序的性能,1方面應盡可能減少創建新對象的次數;同時,還應盡可能減少 T1、T3 的時間,而這些都可以通過對象池技術來實現。所以對象池主要是用來提升性能,在某些情況下,對象池對性能有極大的幫助。但是還有1點需要注意,對象池會增加對象生命周期的復雜度,這是由于從對象池獲得的對象和返還給對象池的對象都沒有真實的創建或燒毀。
  PS:對技術感興趣的同鞋加群5446459721起交換。

設計模式總目錄

  java/android 設計模式學習筆記目錄

特點

  從上面的介紹可以總結:對象池模式適用于“需要使用到大量的同1類對象,這些對象的初始化會消耗大量的系統資源,而且它們只需要使用很短的時間,這類操作會對系統的性能有1定影響”的情況,總結1下就是在下面兩種分配模式下可以選擇使用對象池:

  • 對象以固定的速度不斷地分配,垃圾搜集時間逐漸增加,內存使用率隨之增大;
  • 對象分配存在爆發期,而每次爆發都會致使系統遲滯,并伴隨明顯的GC中斷。
在絕大多數情況下,這些對象要末是數據容器,要末是數據封裝器,其作用是在利用程序和內部消息總線、通訊層或某些API之間充當1個信封。這很常見。例如,數據庫驅動會針對每一個要求和響應創建 Request 和 Response 對象,消息系統會使用 Message 和 Event 封裝器,等等。對象池可以幫助保存和重用這些構造好的對象實例。
  對象池模式會預先創建并初始化好1個對象集適用于重用,當某處需要1個該類型的新對象時,它直接向對象池申請,如果此時對象池中有已預先初始化好的對象,它就直接返回,如果沒有,對象池就會創建1個并且返回;當使用完該對象以后,它會將該對象返還給對象池,對象池會將其重用以免該對象笨重的初始化操作。有1點需要注意的是,1旦1個對象被對象池重用返回給需要使用的地方以后,該對象已存在援用都將會變成非法不可以使用。需要注意的是,在1些系統資源很緊缺的系統上,對象池模式將會限制對象池最大的大小,當已到達最大的數量以后,再次獲得可能會拋出異常或直接被阻塞直到有1個對象被對象池回收。

UML類圖

這里寫圖片描述
  1般對象池模式的 uml 類圖如圖所示,他有3個角色

  • Reusable:對象類,該對象在實際使用中需要頻繁的創建和燒毀,并且初始化操作會很很消耗時間和性能;
  • Client:使用 Reusable 類對象的角色;
  • ReusablePool:管理 Reusable 對象類的所有對象,供 Client 角色使用。
  通常情況下,我們希望所有的 Reusable 對象都會被 ResuablePool 管理,所以可使用單例模式構建 ReusablePool,固然其他的工廠方法模式也是可以的 。Client 調用 getInstance 方法獲得到 ReusablePool 的對象,接著調用 acquireReusable 方法去獲得 Reusable 對象,使用完以后調用 releaseReusable 方法傳入該對象重新交給 ReusablePool 管理。

示例與源碼

  我們先以 android 源碼中的 MessagePool 為例子來分析1下 ObjectPool 在實際開發中的使用效果,以后寫1個小的 demo 。

MessagePool源碼分析

  在 android 中使用 new Message() , Message.obtain() 和 Handler.obtainMessage() 都能夠取得1個 Message 對象,但是為何后兩個的效力會高于前者呢?先來看看源碼的 Message.obtain() 函數:

/** * Return a new Message instance from the global pool. Allows us to * avoid allocating new objects in many cases. */ public static Message obtain() { synchronized (sPoolSync) { if (sPool != null) { Message m = sPool; sPool = m.next; m.next = null; m.flags = 0; // clear in-use flag sPoolSize--; return m; } } return new Message(); }

返回的是 sPool 這個對象,繼續看看 Handler.obtainMessage() 函數:

/** * Returns a new {@link android.os.Message Message} from the global message pool. More efficient than * creating and allocating new instances. The retrieved message has its handler set to this instance (Message.target == this). * If you don't want that facility, just call Message.obtain() instead. */ public final Message obtainMessage() { return Message.obtain(this); }

1樣是調用到了 Message.obtain() 函數,那末我們就從這個函數開始分析, Message 類是如何構建 MessagePool 這個角色的呢?看看這幾處的代碼:

//變量的構建 private static final Object sPoolSync = new Object(); private static Message sPool; private static int sPoolSize = 0; private static final int MAX_POOL_SIZE = 50; private static boolean gCheckRecycle = true;

Message 對象的回收

/** * Return a Message instance to the global pool. * <p> * You MUST NOT touch the Message after calling this function because it has * effectively been freed. It is an error to recycle a message that is currently * enqueued or that is in the process of being delivered to a Handler. * </p> */ public void recycle() { if (isInUse()) { if (gCheckRecycle) { throw new IllegalStateException("This message cannot be recycled because it " + "is still in use."); } return; } recycleUnchecked(); } /** * Recycles a Message that may be in-use. * Used internally by the MessageQueue and Looper when disposing of queued Messages. */ void recycleUnchecked() { // Mark the message as in use while it remains in the recycled object pool. // Clear out all other details. flags = FLAG_IN_USE; what = 0; arg1 = 0; arg2 = 0; obj = null; replyTo = null; sendingUid = -1; when = 0; target = null; callback = null; data = null; synchronized (sPoolSync) { if (sPoolSize < MAX_POOL_SIZE) { next = sPool; sPool = this; sPoolSize++; } } }

從這幾處代碼可以很清楚的看到:

  • sPool 這個靜態對象實際上是相當于構建了1個長度為 MAX_POOL_SIZE 的鏈表, recycleUnchecked 方法之內置鎖的方式(線程安全),判斷當前對象池的大小是不是小于50,若小于50,則會被加到鏈表的頭部,并把 next 對象指向上1次鏈表的頭部;若大于等于50,則直接拋棄掉,那末這些被拋棄的Message將交由GC處理。
  • recycleUnchecked 方法將待回收的Message對象字段先置空,以便節省 MessagePool 的內存和方便下1次的直接使用;
源碼這么1看, android 中 MessagePool 其實實現方式還是很簡單的。

示例

  我們參照上面的 uml 類圖寫1個 demo,首先要定義 Reusable 這個角色,為了直觀,我們將該類定義了很多成員變量,用來摹擬 expensive initialization:
Reusable.class

public class Reusable { public String a; public String b; public String c; public String d; public String e; public String f; public String g; public ArrayList<String> h; public ArrayList<String> i; public ArrayList<String> j; public ArrayList<String> k; public ArrayList<String> l; public Reusable(){ h = new ArrayList<>(); i = new ArrayList<>(); j = new ArrayList<>(); k = new ArrayList<>(); l = new ArrayList<>(); } }

然后用1個 IReusablePool.class接口來定義對象池的基本行動:

public interface IReusablePool { Reusable requireReusable(); void releaseReusable(Reusable reusable); void setMaxPoolSize(int size); }

最后實現該接口:

public class ReusablePool implements IReusablePool { private static final String TAG = "ReusablePool"; private static volatile ReusablePool instance = null; private static List<Reusable> available = new ArrayList<>(); private static List<Reusable> inUse = new ArrayList<>(); private static final byte[] lock = new byte[]{}; private static int maxSize = 5; private int currentSize = 0; private ReusablePool() { available = new ArrayList<>(); inUse = new ArrayList<>(); } public static ReusablePool getInstance() { if (instance == null) { synchronized (ReusablePool.class) { if (instance == null) { instance = new ReusablePool(); } } } return instance; } @Override public Reusable requireReusable() { synchronized (lock) { if (currentSize >= maxSize) { throw new RuntimeException("pool has gotten its maximum size"); } if (available.size() > 0) { Reusable reusable = available.get(0); available.remove(0); currentSize++; inUse.add(reusable); return reusable; } else { Reusable reusable = new Reusable(); inUse.add(reusable); currentSize++; return reusable; } } } @Override public void releaseReusable(Reusable reusable) { if (reusable != null) { reusable.a = null; reusable.b = null; reusable.c = null; reusable.d = null; reusable.e = null; reusable.f = null; reusable.g = null; reusable.h.clear(); reusable.i.clear(); reusable.j.clear(); reusable.k.clear(); reusable.l.clear(); } synchronized (lock) { inUse.remove(reusable); available.add(reusable); currentSize--; } } @Override public void setMaxPoolSize(int size) { synchronized (lock) { maxSize = size; } } }

最后測試程序:

@Override public void onClick(View v) { switch (v.getId()) { case R.id.btn_get: new Thread(new Runnable() { @Override public void run() { try { Reusable reusable = ReusablePool.getInstance().requireReusable(); Log.e("CORRECT", "get a Reusable object " + reusable); Thread.sleep(5000); ReusablePool.getInstance().releaseReusable(reusable); }catch (Exception e){ Log.e("ERROR", e.getMessage()); } } }).start(); break; } }

需要說明的是:

  • releaseReusable 方法不要忘記將 Reusable 對象中的成員變量重置,要不然下次使用會有問題;
  • 摹擬了 5s 的延時,在 demo 中如果到達了 maxPoolSize ,繼續操作是拋出 RunTimeException ,這個在實際使用該進程中可以依照情況更改(可以拋出異常,新建1個對象返回或直接在多線程模式下阻塞,直到有新對象,參考鏈接:
    https://en.wikipedia.org/wiki/Object_pool_pattern#Handling_of_empty_pools);
  • 在程序中使用的是單例模式獲得的 ReusablePool 對象,這個在實際使用進程中也能夠換成工廠方法模式等的其他設計模式。

總結

  對象池模式從上面來分析可以說是用途很大,但是這個模式1定要注意使用的場景,也就是最上面提到的兩點:“對象以固定的速度不斷地分配,垃圾搜集時間逐漸增加,內存使用率隨之增大”和“對象分配存在爆發期,而每次爆發都會致使系統遲滯,并伴隨明顯的GC中斷”,其他的1般情況下最好不要使用對象池模式,我們后面還會提到它的缺點和很多人對其的批評。

優點

  優點上面就已論述的很清楚了,對那些”the rate of instantiation and destruction of a class is high” 和 “the cost of initializing a class instance is high”的場景優化是及其明顯的,例如 database connections,socket connections,threads 和類似于 fonts 和 Bitmap 之類的對象。

圈套

  上面已提過了,使用對象池模式時,每次進行回收操作前都需要將該對象的相干成員變量重置,如果不重置將會致使下1次重復使用該對象時候出現預感不到的毛病,同時的,如果回收對象的成員變量很大,不重置還可能會出現內存 OOM 和信息泄漏等問題。另外的,對象池模式絕大多數情況下都是在多線程中訪問的,所以做好同步工作也是極為重要的。原文:https://en.wikipedia.org/wiki/Object_pool_pattern#Pitfalls
  除以上兩點以外,還需要注意以下問題:

  • 援用泄漏:對象在系統中某個地方注冊了,但沒有返回到池中。
  • 過早回收:消費者已決定將對象返還給對象池,但依然持有它的援用,并試圖履行寫或讀操作,這時候會出現這類情況。
  • 隱式回收:當使用援用計數時可能會出現這類情況。
  • 大小毛病:這類情況在使用字節緩沖區和數組時非常常見:對象應當有不同的大小,而且是以定制的方式構造,但返回對象池后卻作為通用對象重用。
  • 重復下單:這是援用泄漏的1個變種,存在多路復用時特別容易產生:1個對象被分配到多個地方,但其中1個地方釋放了該對象。
  • 就地修改:對象不可變是最好的,但如果不具有那樣做的條件,便可能在讀取對象內容時遇到內容被修改的問題。
  • 縮小對象池:當池中有大量的未使用對象時,要縮小對象池。
對如何根據使用情況放大和縮小對象池的大小,1個廣為人知但鮮有人用的技能:對象池 這篇文章中講述的很清楚,感興趣的可以看看。

討論與批評

  1些開發者不建議在某些語言例如 Java 中使用對象池模式,特別是對象只使用內存并且不會持有其他資源的語言。這些反對者持有的觀點是 new 的操作只需要 10 條指令,而使用對象池模式則需要成百上千條指令,明顯增加了復雜度,而且 GC 操作只會去掃描“活著”的對象援用所指向的內存,而不是它們的成員變量所使用的那塊內存,這就意味著,任何沒有援用的“死亡”對象都能夠被 GC 以很小的代價跳過,相反如果使用對象池模式,持有大量“活著“的對象反而會增加 GC 的時間。原文:https://en.wikipedia.org/wiki/Object_pool_pattern#Criticism

源碼下載

  https://github.com/zhaozepeng/Design-Patterns/tree/master/ObjectPoolPattern

援用

http://www.cnblogs.com/xinye/p/3907642.html
https://en.wikipedia.org/wiki/Object_pool_pattern
http://blog.csdn.net/liyangbing315/article/details/4942870
http://www.infoq.com/cn/news/2015/07/ClojureWerkz
http://blog.csdn.net/xplee0576/article/details/46875555

生活不易,碼農辛苦
如果您覺得本網站對您的學習有所幫助,可以手機掃描二維碼進行捐贈
程序員人生
------分隔線----------------------------
分享到:
------分隔線----------------------------
關閉
程序員人生
主站蜘蛛池模板: 欧美成在线观看 | 欧美高清视频一区 | 秋霞毛片少妇激情免费 | 电影天堂网| 黄网站在线观看 | 成年人视频在线免费观看 | 国产一区二区三区精彩视频 | 国产一区久久 | 91精品久久久久久久久久入口 | yw193.com尤物在线 | 国产成人免费视频网站视频社区 | 国产网站在线免费观看 | 一区二区三区在线免费视频 | 成人国产精品视频 | 成人自拍视频在线 | 国产精品一区二区三区四区 | 久久视频在线看 | 欧美日本片| 国产性―交一乱―色―情人免费看 | 蜜桃视频一区二区 | 久久久久99精品国产片 | 欧美a黄 | 欧美日韩精品二区 | 国产精品白浆 | 日韩视频二区 | 成人免费网站在线观看 | 国产污视频在线 | 网友自拍第一页 | 国产精品18久久久久久久网站 | 国产欧美在线播放 | 九一在线免费观看 | 国产精品一区二区三区久久久 | 国产午夜精品一区二区 | 国产精品av在线 | 国产福利电影网 | 欧美亚洲一区二区三区 | 麻豆传媒一区 | 欧美日韩在线精品 | 国产高清在线视频 | 精品专区| 精品国产乱码久久久久久丨区2区 |