日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢(xún)
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問(wèn)題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷(xiāo)解決方案
Java中的懶惰實(shí)例化與急切實(shí)例化:哪個(gè)更好?

?譯者 | 李睿

審校 | 孫淑娟

人們需要了解如何在Java程序中使用懶惰實(shí)例化和急切實(shí)例化。那么,哪種方法更好?這取決于場(chǎng)景。

當(dāng)實(shí)例化在資源使用方面開(kāi)銷(xiāo)很大的Java對(duì)象時(shí),用戶(hù)不希望每次使用它們時(shí)都必須進(jìn)行實(shí)例化。對(duì)于提高性能來(lái)說(shuō),擁有一個(gè)可以在整個(gè)系統(tǒng)中共享的現(xiàn)成對(duì)象實(shí)例要好得多。在這種情況下,懶惰實(shí)例化策略非常有效。

懶惰實(shí)例化也有一些缺點(diǎn),而在某些系統(tǒng)中,采用急切實(shí)例化方法更好。在急切實(shí)例化中,通常在應(yīng)用程序啟動(dòng)后立即實(shí)例化對(duì)象一次。這兩種方法是不同的。而在某些情況下,某種方法最有效。

本文將介紹這兩種實(shí)例化Java對(duì)象的方法。首先看到代碼示例,然后用Java代碼挑戰(zhàn)測(cè)試所學(xué)到的內(nèi)容。此外,還將討論懶惰實(shí)例化與急切實(shí)例化的優(yōu)缺點(diǎn)。

1.懶惰實(shí)例化的簡(jiǎn)單方法

首先,了解創(chuàng)建單個(gè)實(shí)例并在系統(tǒng)中共享它的簡(jiǎn)單方法:

public static HeroesDB heroesDB;           // #A
private SingletonNaiveApproach() {} // #B

public HeroesDB getHeroesDB() { // #C
if (heroesDB == null) { // #D
heroesDB = new HeroesDB(); // #E
}

return heroesDB; // #F
}
static class HeroesDB { }
}

下面是代碼中發(fā)生的情況:

開(kāi)始(#A),聲明一個(gè)靜態(tài)內(nèi)部類(lèi)HeroesDB。將變量聲明為靜態(tài)的變量,它可以在應(yīng)用程序中共享。

下一步(#B),創(chuàng)建一個(gè)私有構(gòu)造函數(shù),以避免從類(lèi)外部直接實(shí)例化。因此,必須使用getHeroes()方法來(lái)獲取一個(gè)實(shí)例。

在下一行(#C),看到了有效地從HeroesDB返回實(shí)例的方法。

接下來(lái)(#D),檢查heroesDB實(shí)例是否為空。如果是空,將創(chuàng)建一個(gè)新實(shí)例。否則什么也不做。

最后(#F),返回heroesDB對(duì)象實(shí)例。

這種方法適用于小型應(yīng)用程序。然而,在有許多用戶(hù)的大型多線程應(yīng)用程序中,很可能會(huì)出現(xiàn)數(shù)據(jù)沖突。在這種情況下,對(duì)象可能會(huì)被多次實(shí)例化,即使檢查是否為空。以下進(jìn)一步探討為什么會(huì)發(fā)生這種情況的原因。

2.理解競(jìng)態(tài)條件

競(jìng)態(tài)條件是指兩個(gè)或多個(gè)線程并發(fā)競(jìng)爭(zhēng)同一個(gè)變量的情況,這可能會(huì)導(dǎo)致意外的結(jié)果。

在大型多線程應(yīng)用程序中,許多進(jìn)程并行并發(fā)地運(yùn)行。在這種類(lèi)型的應(yīng)用程序中,有可能在另一個(gè)線程實(shí)例化一個(gè)空對(duì)象的同時(shí),一個(gè)線程正在詢(xún)問(wèn)一個(gè)對(duì)象是否為空。在這種情況下,有一個(gè)競(jìng)態(tài)條件,這可能導(dǎo)致重復(fù)的實(shí)例。

可以通過(guò)使用synchronized關(guān)鍵字來(lái)修復(fù)這個(gè)問(wèn)題:

public class SingletonSynchronizedApproach {

public static HeroesDB heroesDB;
private SingletonSynchronizedApproach() {}

public synchronized HeroesDB getHeroesDB() {
if (heroesDB == null) {
heroesDB = new HeroesDB();
}

return heroesDB;
}

static class HeroesDB { }

}

這段代碼解決了線程在getHeroesDB()中存在沖突的問(wèn)題。然而正在同步整個(gè)方法。這可能會(huì)影響性能,因?yàn)槊看沃挥幸粋€(gè)線程能夠訪問(wèn)整個(gè)方法。

以下看看如何解決這個(gè)問(wèn)題。

3.優(yōu)化的多線程懶惰實(shí)例化

要同步getHeroesDB()方法中的策略點(diǎn),需要在該方法中創(chuàng)建同步塊。以下是一個(gè)例子:

public class ThreadSafeSynchronized {

public static volatile HeroesDB heroesDB;

public static HeroesDB getHeroesDB() {
if(heroesDB == null) {
synchronized (ThreadSafeSynchronized.class) {
if(heroesDB == null) {
heroesDB = new HeroesDB();
}
}
}
return heroesDB;
}

static class HeroesDB { }
}

左右滑動(dòng)查看完整代碼

在這段代碼中,只在實(shí)例為空時(shí)同步對(duì)象的創(chuàng)建。否則,將返回對(duì)象實(shí)例。

還要注意的是,同步了ThreadSafeSynchronized類(lèi),因?yàn)槭褂玫氖庆o態(tài)方法。然后再次檢查,以確保heroesDB實(shí)例仍然為空,因?yàn)榭赡苡辛硪粋€(gè)線程已將其實(shí)例化。如果不進(jìn)行雙重檢查,可能會(huì)得到多個(gè)實(shí)例。

另一個(gè)重要的問(wèn)題是,變量heroesDB是不穩(wěn)定的。這意味著不會(huì)緩存變量的值。當(dāng)線程更改這一變量時(shí),它將始終具有最新更新的值。

4.何時(shí)使用急切實(shí)例化

對(duì)于可能從未使用過(guò)的開(kāi)銷(xiāo)大的對(duì)象,最好使用懶惰實(shí)例化。然而,如果所處理的對(duì)象知道在應(yīng)用程序每次啟動(dòng)時(shí)都將被使用,并且就使用的系統(tǒng)資源來(lái)說(shuō),如果創(chuàng)建對(duì)象開(kāi)銷(xiāo)很大,那么最好使用急切實(shí)例化。

假設(shè)必須創(chuàng)建一個(gè)開(kāi)銷(xiāo)非常大的對(duì)象,例如人們總是需要的數(shù)據(jù)庫(kù)連接。等待該對(duì)象被使用可能會(huì)降低應(yīng)用程序的運(yùn)行速度。在這種情況下,急切實(shí)例化更有意義。

5.實(shí)現(xiàn)急切實(shí)例化的簡(jiǎn)單方法

實(shí)現(xiàn)急切實(shí)例化的簡(jiǎn)單方法如下:

public class HeroesDatabaseSimpleEager {

public static final HeroesDB heroesDB = new HeroesDB();

static HeroesDB getHeroesDB() {
return heroesDB;
}

static class HeroesDB {
private HeroesDB() {
System.out.println("Instantiating heroesDB eagerly...");
}

@Override
public String toString() {
return "HeroesDB instance";
}
}

public static void main(String[] args) {
System.out.println(HeroesDatabaseSimpleEager.getHeroesDB());
}
}
The output from this code would be:

Instantiating heroesDB eagerly...
HeroesDB instance

這段代碼的輸出是:

Instantiating heroesDB eagerly...
HeroesDB instance

注意,在本例中沒(méi)有進(jìn)行是為空的檢查。當(dāng)HeroesDB被聲明為HeroesDatabaseSimpleEager中的實(shí)例變量時(shí),它就被實(shí)例化了。因此,每次訪問(wèn)HeroesDatabaseSimpleEager類(lèi)時(shí),都會(huì)從HeroesDB獲得一個(gè)實(shí)例。還重寫(xiě)了toString()方法,以簡(jiǎn)化HeroesDB實(shí)例的輸出。

現(xiàn)在看看使用枚舉實(shí)現(xiàn)急切實(shí)例化的更健壯的方法。

6.使用枚舉創(chuàng)建急切實(shí)例化

使用枚舉是創(chuàng)建急切實(shí)例化對(duì)象的一種更健壯的方法。盡管實(shí)例只會(huì)在枚舉被訪問(wèn)時(shí)被創(chuàng)建,但要注意在下面的代碼中,沒(méi)有對(duì)對(duì)象創(chuàng)建進(jìn)行是否為空的檢查:

public enum HeroesDatabaseEnum {

INSTANCE;
int value;
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
public static void main(String[] args) {
System.out.println(HeroesDatabaseEnum.INSTANCE);

}

這段代碼的輸出將是:

Creating instance...
INSTANCE

這段代碼是線程安全的。它保證只創(chuàng)建一個(gè)實(shí)例,并且序列化對(duì)象,這意味著可以更容易地傳輸它。另一個(gè)細(xì)節(jié)是,對(duì)于枚舉有一個(gè)隱式的私有構(gòu)造函數(shù),這保證了不會(huì)不必要地創(chuàng)建多個(gè)實(shí)例。枚舉被認(rèn)為是使用急切實(shí)例化的最佳方法之一,因?yàn)樗?jiǎn)單而有效。

7.懶惰實(shí)例化vs.急切實(shí)例化

當(dāng)不總是需要實(shí)例化一個(gè)對(duì)象時(shí),采用懶惰實(shí)例化更好。當(dāng)知道總是需要實(shí)例化對(duì)象時(shí),急切實(shí)例化更好。以下是每種方法的優(yōu)缺點(diǎn):

(1)懶惰實(shí)例化

優(yōu)點(diǎn):對(duì)象只會(huì)在需要的時(shí)候被實(shí)例化。

缺點(diǎn):

  • 需要同步才能在多線程環(huán)境中工作。
  • 由于if檢查和同步,性能會(huì)變慢。
  • 當(dāng)需要該對(duì)象時(shí),應(yīng)用程序可能會(huì)有明顯的懶惰。

(2)急切實(shí)例化

優(yōu)點(diǎn):

  • 在大多數(shù)情況下,對(duì)象將在應(yīng)用程序啟動(dòng)時(shí)被實(shí)例化。
  • 使用對(duì)象時(shí)沒(méi)有延遲,因?yàn)樗呀?jīng)被實(shí)例化了。
  • 它在多線程環(huán)境中工作良好。

缺點(diǎn):使用這種方法可能會(huì)不必要地實(shí)例化對(duì)象。

8.Lazy Homer beer創(chuàng)作挑戰(zhàn)

在下面的Java代碼挑戰(zhàn)中,將看到多線程環(huán)境中發(fā)生的懶惰實(shí)例化。

要注意的是,正在使用ThreadPool可以直接使用Thread類(lèi),但最好使用Java并發(fā)API。

根據(jù)在本文中學(xué)到的知識(shí),人們會(huì)認(rèn)為在運(yùn)行以下代碼時(shí)最可能發(fā)生什么情況?

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class LazyHomerBeerCreationChallenge {

public static int i = 0;
public static Beer beer;

static void createBeer() {
if (beer == null) {
try {
Thread.sleep(200);
beer = new Beer();
i++;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

public static void main(String[] args) throws InterruptedException {
ExecutorService executor = Executors.newFixedThreadPool(2);
executor.submit(LazyHomerChallenge::createBeer);
executor.submit(LazyHomerChallenge::createBeer);

executor.awaitTermination(2, TimeUnit.SECONDS);
executor.shutdown();
System.out.println(i);
}

public static class Beer {}
}

以下是應(yīng)對(duì)這一挑戰(zhàn)的選項(xiàng)。仔細(xì)查看代碼并選擇其中一個(gè):

1.A) 1

2.B) 0

3.C) 2

4.D)拋出InterruptedException

9.發(fā)生了什么?懶惰實(shí)例化解釋

這個(gè)代碼挑戰(zhàn)的關(guān)鍵概念是,當(dāng)兩個(gè)線程訪問(wèn)同一個(gè)進(jìn)程時(shí),會(huì)出現(xiàn)并行性。因此,既然有一個(gè)線程。在實(shí)例化beer之前休眠,很可能會(huì)創(chuàng)建兩個(gè)beer實(shí)例。

線程不并發(fā)運(yùn)行的可能性非常小,這取決于JVM實(shí)現(xiàn)。但是由于線程的原因,很有可能最終得到兩個(gè)Thread.sleep的方法。

現(xiàn)在再次查看代碼,注意正在使用線程池創(chuàng)建兩個(gè)線程,然后對(duì)這些線程運(yùn)行createBear方法。

因此,這個(gè)代碼挑戰(zhàn)的正確答案是:C,或2的值。

10.結(jié)論

對(duì)于使用開(kāi)銷(xiāo)大的對(duì)象優(yōu)化性能來(lái)說(shuō),懶惰實(shí)例化和急切實(shí)例化是很重要的概念。以下是關(guān)于這些設(shè)計(jì)策略需要記住的要點(diǎn):

  • 懶惰實(shí)例化需要在實(shí)例化之前進(jìn)行是否為空的檢查。
  • 在多線程環(huán)境中同步對(duì)象以實(shí)現(xiàn)懶惰實(shí)例化。
  • 急切實(shí)例化不需要對(duì)對(duì)象進(jìn)行是否為空的檢查。
  • 使用枚舉是一種有效且簡(jiǎn)單的緊急實(shí)例化方法。

原文鏈接:https://www.infoworld.com/article/3675954/lazy-vs-eager-instantiation-in-java-which-is-better.htm


文章名稱(chēng):Java中的懶惰實(shí)例化與急切實(shí)例化:哪個(gè)更好?
瀏覽路徑:http://m.5511xx.com/article/dpeejoc.html