詳解如何在Java中實(shí)現(xiàn)懶加載
Java 中如何實(shí)現(xiàn)懶加載
懶加載是一種常見(jiàn)的優(yōu)化技術(shù),它可以延遲對(duì)象的創(chuàng)建或初始化,直到對(duì)象第一次被使用時(shí)才進(jìn)行。這種技術(shù)可以幫助我們減少資源的浪費(fèi),提高程序的運(yùn)行效率。
在 Scala 中,我們可以使用關(guān)鍵字 lazy
來(lái)定義惰性變量,實(shí)現(xiàn)延遲加載(懶加載)。但是在 Java 中,我們需要使用其他的技術(shù)來(lái)實(shí)現(xiàn)懶加載。在本文中,我們將介紹如何使用 Java 中的 Supplier 接口和雙重檢查鎖定模式來(lái)實(shí)現(xiàn)懶加載,并保證只初始化一次。
使用 Supplier 接口實(shí)現(xiàn)懶加載
Java 中的 Supplier 接口是一個(gè)函數(shù)式接口,用于提供類(lèi)型為 T 的對(duì)象。我們可以通過(guò)傳遞一個(gè) lambda 表達(dá)式給 Supplier 接口的實(shí)例來(lái)實(shí)現(xiàn)懶加載。
下面是一個(gè)使用 Supplier 接口實(shí)現(xiàn)懶加載的示例代碼:
import java.util.function.Supplier; public class Lazy<T> { private final Supplier<T> supplier; private T value; public Lazy(Supplier<T> supplier) { this.supplier = supplier; } public T get() { if (value == null) { value = supplier.get(); } return value; } }
在上面的代碼中,我們定義了一個(gè)泛型類(lèi) Lazy<T>
,并在構(gòu)造函數(shù)中傳入一個(gè) Supplier<T>
對(duì)象。在 get()
方法中,我們使用 value
變量來(lái)緩存 T
類(lèi)型的對(duì)象,并在需要時(shí)調(diào)用 supplier.get()
方法獲取 T
類(lèi)型的對(duì)象。由于 value
變量只會(huì)被初始化一次,因此能夠保證只有在需要時(shí)才會(huì)初始化 value
變量。
以下是使用 Lazy
類(lèi)的示例代碼:
public class LazyDemo { public static void main(String[] args) { Lazy<String> lazyString = new Lazy<>(() -> { System.out.println("Initializing lazy string..."); return "Hello, World!"; }); System.out.println(lazyString.get()); System.out.println(lazyString.get()); System.out.println(lazyString.get()); } }
在上面的代碼中,我們創(chuàng)建了一個(gè) Lazy<String>
對(duì)象,并傳入一個(gè) lambda 表達(dá)式,用于提供 String
類(lèi)型的對(duì)象。在 main()
方法中,我們多次調(diào)用 lazyString.get()
方法,并打印返回值。由于 value
變量只會(huì)被初始化一次,因此只有在第一次調(diào)用 lazyString.get()
方法時(shí)會(huì)輸出 "Initializing lazy string...",后續(xù)調(diào)用時(shí)不會(huì)輸出。
使用雙重檢查鎖定模式實(shí)現(xiàn)懶加載
雙重檢查鎖定模式是一種常見(jiàn)的用于實(shí)現(xiàn)懶加載的技術(shù)。它利用了同步塊和 volatile 關(guān)鍵字來(lái)確保線程安全和懶加載。
以下是使用雙重檢查鎖定模式實(shí)現(xiàn)懶加載
第一步,創(chuàng)建一個(gè)Java類(lèi),并聲明一個(gè)泛型類(lèi)型,以存儲(chǔ)惰性計(jì)算的值。在我們的示例中,我們將使用泛型類(lèi)型T,以便我們可以使用Lazy類(lèi)來(lái)存儲(chǔ)任何類(lèi)型的值。
public class Lazy<T> { private final Supplier<T> supplier; private volatile T result; public Lazy(Supplier<T> supplier) { this.supplier = supplier; } public T get() { T value = result; if (value == null) { synchronized (this) { value = result; if (value == null) { value = supplier.get(); result = value; } } } return value; } }
在上面的代碼中,我們聲明了一個(gè)私有Supplier類(lèi)型的成員變量supplier,它將計(jì)算值的函數(shù)作為參數(shù)傳遞。我們還聲明了一個(gè)volatile類(lèi)型的成員變量result,用于存儲(chǔ)計(jì)算的結(jié)果,并確保在多線程環(huán)境下正確使用。
第二步,實(shí)現(xiàn)惰性加載的邏輯。在我們的Lazy類(lèi)中,我們實(shí)現(xiàn)了一個(gè)get()方法,該方法返回計(jì)算結(jié)果。在get()方法中,我們使用了雙重檢查鎖定機(jī)制來(lái)確保懶加載的正確性。在第一次調(diào)用get()方法時(shí),我們檢查result變量是否為空。如果為空,我們使用synchronized代碼塊來(lái)避免多個(gè)線程同時(shí)計(jì)算值。在synchronized代碼塊中,我們?cè)俅螜z查result變量是否為空,以確保在鎖定時(shí)另一個(gè)線程未計(jì)算出值。如果為空,我們調(diào)用supplier.get()方法來(lái)計(jì)算值,并將結(jié)果存儲(chǔ)在result變量中。在計(jì)算完成后,我們將值返回給調(diào)用者。
第三步,使用單例模式確保只初始化一次。為了確保只初始化一次,我們將result變量聲明為volatile類(lèi)型,并使用雙重檢查鎖定機(jī)制。在計(jì)算值的過(guò)程中,如果另一個(gè)線程已經(jīng)計(jì)算了值,則返回先前計(jì)算的結(jié)果。
第四步,測(cè)試我們的Lazy類(lèi)是否按預(yù)期工作。在測(cè)試中,我們將創(chuàng)建一個(gè)名為T(mén)estLazy的類(lèi),并聲明一個(gè)Lazy類(lèi)型的變量,然后將一個(gè)匿名函數(shù)傳遞給Lazy類(lèi)的構(gòu)造函數(shù)來(lái)計(jì)算一個(gè)值。我們將使用該變量的值來(lái)測(cè)試惰性加載和單例模式的正確性。
來(lái)計(jì)算一個(gè)值。我們將使用該變量的值來(lái)測(cè)試惰性加載和單例模式的正確性。
public class TestLazy { public static void main(String[] args) { Lazy<Integer> lazyValue = new Lazy<>(() -> { int result = 100 + 200; System.out.println("Calculating value..."); return result; }); System.out.println("Before calling get()"); // 第一次調(diào)用 int value1 = lazyValue.get(); System.out.println("After calling get()"); // 第二次調(diào)用 int value2 = lazyValue.get(); System.out.println("After calling get() again"); System.out.println("value1: " + value1); System.out.println("value2: " + value2); // 判斷是否為同一個(gè)對(duì)象 System.out.println("Is same instance: " + (lazyValue == lazyValue)); } }
運(yùn)行該測(cè)試類(lèi)后,我們期望看到的輸出是:
vbnetCopy code
Before calling get()
Calculating value...
After calling get()
After calling get() again
value1: 300
value2: 300
Is same instance: true
輸出表明,第一次調(diào)用get()方法時(shí),計(jì)算值的函數(shù)被調(diào)用并計(jì)算出值。在第二次調(diào)用get()方法時(shí),我們沒(méi)有看到“Calculating value…”這個(gè)輸出,這證明了惰性加載的正確性。此外,我們還檢查了兩次獲取到的值是否相等,以及對(duì)象是否是同一個(gè)實(shí)例,這證明了單例模式的正確性。
最后,我們現(xiàn)在已經(jīng)有了一個(gè)實(shí)現(xiàn)懶加載的Lazy類(lèi),該類(lèi)使用Supplier接口實(shí)現(xiàn)了惰性加載和單例模式,使得我們可以輕松地延遲計(jì)算值,同時(shí)避免了多次初始化變量的問(wèn)題。
到此這篇關(guān)于詳解如何在Java中實(shí)現(xiàn)懶加載的文章就介紹到這了,更多相關(guān)Java懶加載內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
深入了解Java線程池:從設(shè)計(jì)思想到源碼解讀
這篇文章將從設(shè)計(jì)思想到源碼解讀,帶大家深入了解Java的線程池,文中的示例代碼講解詳細(xì),對(duì)我們的學(xué)習(xí)或工作有一定的幫助,需要的可以參考一下2021-12-12詳解Java枚舉類(lèi)在生產(chǎn)環(huán)境中的使用方式
本文主要介紹了Java枚舉類(lèi)在生產(chǎn)環(huán)境中的使用方式,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-02-02Java快速入門(mén)掌握類(lèi)與對(duì)象及變量的使用
類(lèi)和對(duì)象是兩種以計(jì)算機(jī)為載體的計(jì)算機(jī)語(yǔ)言的合稱(chēng)。對(duì)象是對(duì)客觀事物的抽象,類(lèi)是對(duì)對(duì)象的抽象。類(lèi)是一種抽象的數(shù)據(jù)類(lèi)型;變量就是可以變化的量,存儲(chǔ)在內(nèi)存中—個(gè)可以擁有在某個(gè)范圍內(nèi)的可變存儲(chǔ)區(qū)域2022-04-04Java中內(nèi)存異常StackOverflowError與OutOfMemoryError詳解
這篇文章主要介紹了 Java中內(nèi)存異常StackOverflowError與OutOfMemoryError詳解的相關(guān)資料,需要的朋友可以參考下2017-03-03SpringBoot?項(xiàng)目打成?jar后加載外部配置文件的操作方法
這篇文章主要介紹了SpringBoot?項(xiàng)目打成?jar后加載外部配置文件的操作方法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-03-03SpringBoot整合Mybatis實(shí)現(xiàn)CRUD
這篇文章主要介紹了SpringBoot整合Mybatis實(shí)現(xiàn)CRUD的相關(guān)知識(shí),本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-09-09Java編程通過(guò)匹配合并數(shù)據(jù)實(shí)例解析(數(shù)據(jù)預(yù)處理)
這篇文章主要介紹了Java編程通過(guò)匹配合并數(shù)據(jù)實(shí)例解析(數(shù)據(jù)預(yù)處理),分享了相關(guān)代碼示例,小編覺(jué)得還是挺不錯(cuò)的,具有一定借鑒價(jià)值,需要的朋友可以參考下2018-01-01基于指針pointers和引用references的區(qū)別分析
本篇文章介紹了,基于指針pointers和引用references的區(qū)別分析。需要的朋友參考下2013-05-05