CodeGym /Java Blog /Random /Java Singleton Class
John Squirrels
Antas
San Francisco

Java Singleton Class

Nai-publish sa grupo
Hi! Ngayon ay susuriin natin ang mga detalye ng iba't ibang pattern ng disenyo, simula sa pattern ng Java Singleton. Suriin natin: ano ang alam natin tungkol sa mga pattern ng disenyo sa pangkalahatan? Ang mga pattern ng disenyo ay mga pinakamahusay na kasanayan na maaari naming ilapat upang malutas ang ilang kilalang problema. Ang mga pattern ng disenyo ay karaniwang hindi nakatali sa anumang programming language. Isipin ang mga ito bilang isang hanay ng mga rekomendasyon upang matulungan kang maiwasan ang mga pagkakamali at maiwasan ang muling pag-imbento ng gulong.Mga pattern ng disenyo: Singleton - 1

Ano ang singleton sa Java?

Ang Singleton ay isa sa pinakasimpleng pattern ng disenyo sa antas ng klase. Minsan sinasabi ng mga tao na "singleton ang klase na ito", ibig sabihin, ipinapatupad ng klase ang pattern ng disenyo ng singleton. Minsan kinakailangan na magsulat ng klase kung saan nililimitahan natin ang instantiation sa isang bagay. Halimbawa, isang klase na responsable sa pag-log o pagkonekta sa isang database. Ang singleton design pattern ay naglalarawan kung paano natin ito makakamit. Ang singleton ay isang design pattern na gumagawa ng dalawang bagay:
  1. Tinitiyak nito na magkakaroon lamang ng isang pagkakataon ng klase.

  2. Nagbibigay ito ng isang punto ng pandaigdigang pag-access sa pagkakataong iyon.

Samakatuwid, mayroong dalawang tampok na katangian ng halos bawat pagpapatupad ng singleton pattern:
  1. Isang pribadong constructor. Nililimitahan nito ang kakayahang lumikha ng mga bagay ng klase sa labas ng klase mismo.

  2. Isang pampublikong static na pamamaraan na nagbabalik ng instance ng klase. Ang pamamaraang ito ay tinatawag na getInstance . Ito ang punto ng pandaigdigang pag-access sa instance ng klase.

Mga opsyon sa pagpapatupad

Ang pattern ng disenyo ng singleton ay inilalapat sa iba't ibang paraan. Ang bawat pagpipilian ay mabuti at masama sa sarili nitong paraan. Gaya ng dati, walang perpektong opsyon dito, ngunit dapat tayong magsikap para sa isa. Una sa lahat, magpasya tayo kung ano ang bumubuo ng mabuti at masama, at kung anong mga sukatan ang nakakaapekto sa kung paano natin tinatasa ang iba't ibang pagpapatupad ng pattern ng disenyo. Magsimula tayo sa mabuti. Narito ang mga salik na ginagawang mas makatas at kaakit-akit ang pagpapatupad:
  • Lazy initialization: hindi nagagawa ang instance hangga't hindi ito kinakailangan.

  • Simple at transparent na code: ang panukat na ito, siyempre, ay subjective, ngunit ito ay mahalaga.

  • Kaligtasan ng thread: tamang operasyon sa isang multi-threaded na kapaligiran.

  • Mataas na pagganap sa isang multi-threaded na kapaligiran: kaunti o walang pag-block ng thread kapag nagbabahagi ng mapagkukunan.

Ngayon ang cons. Ililista namin ang mga salik na naglalagay sa isang pagpapatupad sa masamang liwanag:
  • Walang tamad na pagsisimula: kapag ang klase ay na-load kapag nagsimula ang aplikasyon, hindi alintana kung ito ay kinakailangan o hindi (paradoxically, sa IT mundo mas mahusay na maging tamad)

  • Kumplikado at mahirap basahin ang code. Ang sukatan na ito ay subjective din. Kung magsisimulang dumugo ang iyong mga mata, ipagpalagay namin na ang pagpapatupad ay hindi ang pinakamahusay.

  • Kakulangan ng kaligtasan ng thread. Sa madaling salita, "panganib sa thread". Maling operasyon sa isang multi-threaded na kapaligiran.

  • Hindi magandang pagganap sa isang multi-threaded na kapaligiran: ang mga thread ay humaharang sa bawat isa sa lahat ng oras o madalas kapag nagbabahagi ng mapagkukunan.

Code

Ngayon handa na kaming isaalang-alang ang iba't ibang mga opsyon sa pagpapatupad at ipahiwatig ang mga kalamangan at kahinaan:

Simple


public class Singleton {
    private static final Singleton INSTANCE = new Singleton();
    
    private Singleton() {
    }
    
    public static Singleton getInstance() {
        return INSTANCE;
    }
}
Ang pinakasimpleng pagpapatupad. Mga kalamangan:
  • Simple at transparent na code

  • Kaligtasan ng thread

  • Mataas na pagganap sa isang multi-threaded na kapaligiran

Cons:
  • Walang tamad na pagsisimula.
Sa pagtatangkang ayusin ang nakaraang pagkukulang, nakukuha namin ang pangalawang numero ng pagpapatupad:

Tamad na pagsisimula


public class Singleton {
  private static final Singleton INSTANCE;

  private Singleton() {}

  public static Singleton getInstance() {
    if (INSTANCE == null) {
      INSTANCE = new Singleton();
    }
    return INSTANCE;
  }
}
Mga kalamangan:
  • Tamad na pagsisimula.

Cons:
  • Hindi ligtas sa thread

Ang pagpapatupad na ito ay kawili-wili. Maaari naming simulan ang tamad, ngunit nawalan kami ng kaligtasan ng thread. Huwag mag-alala — pinag-synchronize namin ang lahat sa ikatlo na pagpapatupad.

Naka-synchronize na pag-access


public class Singleton {
  private static final Singleton INSTANCE;

  private Singleton() {
  }

  public static synchronized Singleton getInstance() {
    if (INSTANCE == null) {
      INSTANCE = new Singleton();
    }
    return INSTANCE;
  }
}
Mga kalamangan:
  • Tamad na pagsisimula.

  • Kaligtasan ng thread

Cons:
  • Hindi magandang pagganap ng multithreaded

Magaling! Sa pagpapatupad bilang tatlo, ibinabalik namin ang kaligtasan ng thread! Siyempre, ito ay mabagal... Ngayon ang getInstance na paraan ay naka-synchronize, kaya maaari itong isagawa sa pamamagitan lamang ng isang thread sa isang pagkakataon. Sa halip na i-synchronize ang buong pamamaraan, kailangan lang talaga nating i-synchronize ang bahagi nito na nagpapasimula sa bagong instance. Ngunit hindi lang natin magagamit ang isang naka-synchronize na bloke para balutin ang bahaging responsable sa paglikha ng bagong instance. Ang paggawa nito ay hindi masisiguro ang kaligtasan ng thread. Medyo mas kumplikado ang lahat. Ang wastong pag-synchronize ay makikita sa ibaba:

I-double check ang pag-lock


public class Singleton {
    private static final Singleton INSTANCE;

  private Singleton() {
  }

    public static Singleton getInstance() {
        if (INSTANCE == null) {
            synchronized (Singleton.class) {
                if (INSTANCE == null) {
                    INSTANCE = new Singleton();
                }
            }
        }
        return INSTANCE;
    }
}
Mga kalamangan:
  • Tamad na pagsisimula.

  • Kaligtasan ng thread

  • Mataas na pagganap sa isang multi-threaded na kapaligiran

Cons:
  • Hindi suportado sa mga naunang bersyon ng Java na mas mababa sa 1.5 (ang paggamit ng pabagu-bagong keyword ay naayos mula noong 1.5 na bersyon)

Tandaan na para gumana nang tama ang opsyon sa pagpapatupad na ito, dapat matugunan ang isa sa dalawang kundisyon. Ang variable na INSTANCE ay dapat na pinal o pabagu-bago . Ang huling pagpapatupad na tatalakayin natin ngayon ay ang singleton na may hawak ng klase .

May hawak ng klase


public class Singleton {

   private Singleton() {
   }

   private static class SingletonHolder {
       public static final Singleton HOLDER_INSTANCE = new Singleton();
   }

   public static Singleton getInstance() {
       return SingletonHolder.HOLDER_INSTANCE;
   }
}
Mga kalamangan:
  • Tamad na pagsisimula.

  • Kaligtasan ng thread.

  • Mataas na pagganap sa isang multi-threaded na kapaligiran.

Cons:
  • Ang tamang operasyon ay nangangailangan ng garantiya na ang singleton object ay sinisimulan nang walang mga error. Kung hindi, ang unang tawag sa getInstance na paraan ay magreresulta sa isang ExceptionInInitializerError , at lahat ng kasunod na tawag ay magbubunga ng NoClassDefFoundError .

Ang pagpapatupad na ito ay halos perpekto. Ito ay tamad, at ligtas ang thread, at mabilis. Ngunit mayroon itong nuance, tulad ng ipinaliwanag sa listahan ng mga kahinaan. Paghahambing ng iba't ibang pagpapatupad ng singleton pattern:
Pagpapatupad Tamad na pagsisimula Kaligtasan ng thread Multithreaded na pagganap Kailan gagamitin?
Simple - + Mabilis Hindi kailanman. O posibleng kapag ang tamad na pagsisimula ay hindi mahalaga. Ngunit hindi kailanman magiging mas mahusay.
Tamad na pagsisimula + - Hindi maaari Laging kapag hindi kailangan ang multithreading
Naka-synchronize na pag-access + + Mabagal Hindi kailanman. O posibleng kapag ang multithreaded na pagganap ay hindi mahalaga. Ngunit hindi kailanman magiging mas mahusay.
I-double check ang pag-lock + + Mabilis Sa mga bihirang kaso kapag kailangan mong pangasiwaan ang mga exception kapag lumilikha ng singleton (kapag hindi naaangkop ang singleton na may hawak ng klase)
May hawak ng klase + + Mabilis Sa tuwing kailangan ang multithreading at may garantiya na malilikha ang singleton object nang walang problema.

Mga kalamangan at kahinaan ng singleton pattern

Sa pangkalahatan, ginagawa ng isang singleton ang eksaktong inaasahan dito:
  1. Tinitiyak nito na magkakaroon lamang ng isang pagkakataon ng klase.

  2. Nagbibigay ito ng isang punto ng pandaigdigang pag-access sa pagkakataong iyon.

Gayunpaman, ang pattern na ito ay may mga pagkukulang:
  1. Ang singleton ay lumalabag sa iisang responsibilidad na prinsipyo: bilang karagdagan sa mga direktang tungkulin nito, kinokontrol din ng singleton class ang bilang ng mga pagkakataon.

  2. Ang pag-asa ng isang ordinaryong klase sa isang singleton ay hindi makikita sa pampublikong kontrata ng klase.

  3. Ang mga global variable ay masama. Sa huli, ang isang singleton ay nagiging isang mabigat na global variable.

  4. Ang pagkakaroon ng singleton ay binabawasan ang testability ng application sa kabuuan at ang mga klase na gumagamit ng singleton sa partikular.

At ayun na nga! :) Na-explore namin ang Java Singleton Class kasama mo. Ngayon, para sa natitirang bahagi ng iyong buhay, kapag nakikipag-usap sa iyong mga kaibigan sa programmer, maaari mong banggitin hindi lamang kung gaano kahusay ang pattern, kundi pati na rin ang ilang mga salita tungkol sa kung ano ang nagiging masama. Good luck sa pag-master ng bagong kaalaman na ito.

Karagdagang pagbabasa:

Mga komento
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION