延迟加载原理与实现
一、延遲加載的定義與原理
延遲加載是開發(fā)過程中靈活獲取對象的一種求值策略,該策略在定義目標(biāo)對象時并不會立即計算實際對象值,而是在該對象后續(xù)被實際調(diào)用時才去求值。在計算機科學(xué)中,延遲加載對應(yīng)一個專門術(shù)語:惰性求值,其維基百科定義如下。
在編程語言理論中,惰性求值(Lazy Evaluation),又譯為惰性計算、懶惰求值,也稱為傳需求調(diào)用(call-by-need),是計算機編程中的一個概念,目的是要最小化計算機要做的工作。延遲求值特別用于匿名式函數(shù)編程,在使用延遲求值的時候,表達式不在它被綁定到變量之后就立即求值,而是在該值被取用的時候求值。
簡單來說,延遲加載就是指表達式只在必要時才求值,而非被賦給某個變量時立即求值。與惰性求值相對應(yīng)的是及早求值,其維基百科定義如下。
及早求值(Eager evaluation)又譯熱切求值,也被稱為貪婪求值(Greedy evaluation),是多數(shù)傳統(tǒng)編程語言的求值策略。在及早求值中,表達式在它被約束到變量的時候就立即求值。這在簡單編程語言中作為低層策略是更有效率的,因為不需要建造和管理表示未求值的表達式的中介數(shù)據(jù)結(jié)構(gòu)。
二、延遲加載的好處與應(yīng)用場景
對于及早求值,對象在定義時便已獲取,若后續(xù)邏輯并未使用該對象,則會造成資源浪費,降低運行效率。而惰性求值會在數(shù)據(jù)真正被調(diào)用時去獲取,若后續(xù)未調(diào)用則不獲取,避免計算開銷,若后續(xù)調(diào)用多次,也可通過存儲計算結(jié)果的方式來實現(xiàn)結(jié)果復(fù)用,避免多次計算。延遲加載流程圖如下所示。
對于業(yè)務(wù)場景中計算開銷較大的數(shù)據(jù)對象,若其在后續(xù)邏輯中可能會根據(jù)不同的業(yè)務(wù)判斷條件在不同作用域中被使用多次,也可能一次都不會被使用到,那就可以使用延遲加載來獲取該對象。
如上述代碼舉例,Heavy為計算開銷較大的對象。
- 若采用立即加載,在某些特定業(yè)務(wù)條件下,conditionA與conditionB均為false,此時最終并未使用Heavy對象,則會造成資源浪費,若代碼改為在各自條件的作用域中單獨獲取Heavy對象,則有可能造成重復(fù)獲取,也會造成多余計算。
- 若采用延遲加載,當(dāng)條件均不滿足時,不會調(diào)用Heavy對象,此時也沒有觸發(fā)真正加載操作,當(dāng)存在條件滿足時,會在第一次調(diào)用對象時進行加載操作,后續(xù)如還需使用可復(fù)用該次加載結(jié)果,整個過程沒有資源浪費。
三、延遲加載的實現(xiàn)
Java語言中并沒有直接的延遲加載方法,但在Java8中引入的lambda表達式以及Supplier等函數(shù)式接口為我們實現(xiàn)延遲操作提供了很大的便捷性。可以通過增加一個間接層來實現(xiàn),相當(dāng)于使用Proxy模式,利用Supplier定義計算邏輯,把耗資源的運算過程放入Supplier的get方法中,并在Proxy對象中持有該Supplier實例,由Proxy對象來維護目標(biāo)對象的實際加載與結(jié)果緩存,并確保安全性(如線程安全等)。一種可行的實現(xiàn)方案如下所示。
其中Lazy即為外層Proxy對象,內(nèi)部持有Supplier實例delegate,通過volatile和雙重檢驗鎖實現(xiàn)目標(biāo)對象的單例效果,保證最多只加載一次,實現(xiàn)結(jié)果緩存復(fù)用,并確保多線程并發(fā)環(huán)境下的訪問安全性。同時還提供了一系列便捷操作,如映射/扁平化映射/過濾等操作,且仍返回延遲對象,同樣具有延遲加載效果,方便開發(fā)使用。
除了上述實現(xiàn)方案之外,延遲加載也可通過其它類似方式實現(xiàn),例如下述代碼,通過運行時改變內(nèi)部Supplier變量的對象類型來判斷是否已加載對象并返回,并采用synchronized同步方法來實現(xiàn)線程安全。
還有一種實現(xiàn)更為簡便,代碼如下所示,利用Java中ConcurrentHashMap本身的同步機制來實現(xiàn)線程安全。
參考
- 關(guān)于延遲加載的一些應(yīng)用
總結(jié)
 
                            
                        - 上一篇: Visual Studio 类向导HRE
- 下一篇: NES模拟器开发笔记(001)缘起、资料
