जावा में सिंगलटन पैटर्न को लागू करने का एक प्रभावी तरीका क्या है?
जावा में सिंगलटन पैटर्न को लागू करने का एक प्रभावी तरीका क्या है?
जवाबों:
एक एनम का प्रयोग करें:
public enum Foo {
INSTANCE;
}
जोशुआ बलोच ने Google I / O 2008 में अपने प्रभावी जावा रीलोडेड टॉक: वीडियो के लिंक में इस दृष्टिकोण को समझाया । इसके अलावा स्लाइड देख उसकी प्रस्तुति (के 30-32 effective_java_reloaded.pdf ):
एक सीरियल सिंगनल को लागू करने का सही तरीका
public enum Elvis { INSTANCE; private final String[] favoriteSongs = { "Hound Dog", "Heartbreak Hotel" }; public void printFavorites() { System.out.println(Arrays.toString(favoriteSongs)); } }
संपादित करें: "प्रभावी जावा" का एक ऑनलाइन भाग कहता है:
"यह दृष्टिकोण कार्यात्मक रूप से सार्वजनिक क्षेत्र के दृष्टिकोण के बराबर है, सिवाय इसके कि यह अधिक संक्षिप्त है, मुफ्त के लिए क्रमबद्धता मशीनरी प्रदान करता है, और परिष्कृत क्रमांकन या प्रतिबिंब हमलों के सामने भी, कई तात्कालिकता के खिलाफ एक विडंबना गारंटी प्रदान करता है। जबकि यह दृष्टिकोण। अभी तक व्यापक रूप से अपनाया जा रहा है, एक एकल-तत्व एनम प्रकार एक सिंगलटन को लागू करने का सबसे अच्छा तरीका है । "
उपयोग के आधार पर, कई "सही" उत्तर हैं।
Java5 के बाद से इसे करने का सबसे अच्छा तरीका एक एनम का उपयोग करना है:
public enum Foo {
INSTANCE;
}
प्री जावा 5, सबसे सरल मामला है:
public final class Foo {
private static final Foo INSTANCE = new Foo();
private Foo() {
if (INSTANCE != null) {
throw new IllegalStateException("Already instantiated");
}
}
public static Foo getInstance() {
return INSTANCE;
}
public Object clone() throws CloneNotSupportedException{
throw new CloneNotSupportedException("Cannot clone instance of this class");
}
}
कोड पर चलते हैं। पहला, आप चाहते हैं कि कक्षा अंतिम हो। इस मामले में, मैंने final
उपयोगकर्ताओं को यह बताने के लिए कीवर्ड का उपयोग किया है कि यह अंतिम है। फिर आपको उपयोगकर्ताओं को अपना फू बनाने के लिए रोकने के लिए कंस्ट्रक्टर को निजी बनाना होगा। कंस्ट्रक्टर से एक अपवाद को फेंकना उपयोगकर्ताओं को दूसरा फू बनाने के लिए प्रतिबिंब का उपयोग करने से रोकता है। तब आप private static final Foo
केवल उदाहरण के लिए एक फ़ील्ड बनाते हैं , और public static Foo getInstance()
इसे वापस करने के लिए एक विधि। जावा विनिर्देश यह सुनिश्चित करता है कि निर्माणकर्ता को केवल तब बुलाया जाता है जब कक्षा पहली बार उपयोग की जाती है।
जब आपके पास एक बहुत बड़ी वस्तु या भारी निर्माण कोड होता है और अन्य सुलभ स्थैतिक विधियाँ या क्षेत्र होते हैं, जिनका उपयोग एक उदाहरण से पहले किया जा सकता है, तब और उसके बाद ही आपको आलसी आरंभीकरण का उपयोग करने की आवश्यकता होती है।
आप private static class
उदाहरण लोड करने के लिए a का उपयोग कर सकते हैं । फिर कोड इस तरह दिखेगा:
public final class Foo {
private static class FooLoader {
private static final Foo INSTANCE = new Foo();
}
private Foo() {
if (FooLoader.INSTANCE != null) {
throw new IllegalStateException("Already instantiated");
}
}
public static Foo getInstance() {
return FooLoader.INSTANCE;
}
}
चूंकि लाइन private static final Foo INSTANCE = new Foo();
को केवल तब निष्पादित किया जाता है जब क्लास फूलाडेर का उपयोग वास्तव में किया जाता है, यह आलसी तात्कालिकता का ख्याल रखता है, और क्या यह थ्रेड सुरक्षित होने की गारंटी है।
जब आप भी अपनी वस्तु को क्रमबद्ध करने में सक्षम होना चाहते हैं, तो आपको यह सुनिश्चित करने की आवश्यकता है कि deserialization एक प्रतिलिपि नहीं बनाएगा।
public final class Foo implements Serializable {
private static final long serialVersionUID = 1L;
private static class FooLoader {
private static final Foo INSTANCE = new Foo();
}
private Foo() {
if (FooLoader.INSTANCE != null) {
throw new IllegalStateException("Already instantiated");
}
}
public static Foo getInstance() {
return FooLoader.INSTANCE;
}
@SuppressWarnings("unused")
private Foo readResolve() {
return FooLoader.INSTANCE;
}
}
विधि readResolve()
सुनिश्चित करेगी कि एकमात्र उदाहरण वापस कर दिया जाएगा, तब भी जब ऑब्जेक्ट को आपके प्रोग्राम के पिछले भाग में क्रमबद्ध किया गया था।
डिस्क्लेमर: मैंने सभी भयानक उत्तरों को संक्षेप में लिखा है और इसे अपने शब्दों में लिखा है।
सिंगलटन लागू करते समय हमारे पास 2 विकल्प हैं
1. आलसी लोडिंग
2. प्रारंभिक लोडिंग
आलसी लोडिंग बिट ओवरहेड (बहुत ईमानदार होने के लिए) जोड़ता है, इसलिए इसका उपयोग केवल तब करें जब आपके पास एक बहुत बड़ी वस्तु या भारी निर्माण कोड हो और इसमें अन्य सुलभ स्थिर विधियाँ या फ़ील्ड भी हों जिनका उपयोग किसी उदाहरण से पहले किया जा सकता है, तब और तब ही आपको आलसी आरंभीकरण का उपयोग करने की आवश्यकता है। जल्दी लोडिंग का चयन करना एक अच्छा विकल्प है।
सिंगलटन को लागू करने का सबसे सरल तरीका है
public class Foo {
// It will be our sole hero
private static final Foo INSTANCE = new Foo();
private Foo() {
if (INSTANCE != null) {
// SHOUT
throw new IllegalStateException("Already instantiated");
}
}
public static Foo getInstance() {
return INSTANCE;
}
}
इसके शुरुआती लोडेड सिंगलटन को छोड़कर सब कुछ अच्छा है। चलो आलसी लोड सिंगलटन की कोशिश करो
class Foo {
// Our now_null_but_going_to_be sole hero
private static Foo INSTANCE = null;
private Foo() {
if (INSTANCE != null) {
// SHOUT
throw new IllegalStateException("Already instantiated");
}
}
public static Foo getInstance() {
// Creating only when required.
if (INSTANCE == null) {
INSTANCE = new Foo();
}
return INSTANCE;
}
}
अब तक तो अच्छा है लेकिन हमारा हीरो कई बुरे धागों के साथ अकेले लड़ते हुए नहीं बचेगा, जो हमारे हीरो के कई उदाहरण चाहते हैं। तो इसे बुराई मल्टी थ्रेडिंग से बचाने की सुविधा देता है
class Foo {
private static Foo INSTANCE = null;
// TODO Add private shouting constructor
public static Foo getInstance() {
// No more tension of threads
synchronized (Foo.class) {
if (INSTANCE == null) {
INSTANCE = new Foo();
}
}
return INSTANCE;
}
}
लेकिन यह नायक को बचाने के लिए पर्याप्त नहीं है, वास्तव में !!! यह हमारे नायक की मदद करने के लिए सबसे अच्छा हम कर सकते हैं / करना चाहिए
class Foo {
// Pay attention to volatile
private static volatile Foo INSTANCE = null;
// TODO Add private shouting constructor
public static Foo getInstance() {
if (INSTANCE == null) { // Check 1
synchronized (Foo.class) {
if (INSTANCE == null) { // Check 2
INSTANCE = new Foo();
}
}
}
return INSTANCE;
}
}
इसे "डबल-चेक्ड लॉकिंग मुहावरा" कहा जाता है। वाष्पशील कथन को भूलना आसान है और यह समझना मुश्किल है कि यह क्यों आवश्यक है।
जानकारी के लिए: http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html
अब हम बुराई के धागे के बारे में निश्चित हैं लेकिन क्रूर क्रम के बारे में क्या? हमें यह सुनिश्चित करना होगा कि डे-सीरियलियास्टियन कोई नई वस्तु न बने
class Foo implements Serializable {
private static final long serialVersionUID = 1L;
private static volatile Foo INSTANCE = null;
// Rest of the things are same as above
// No more fear of serialization
@SuppressWarnings("unused")
private Object readResolve() {
return INSTANCE;
}
}
विधि readResolve()
यह सुनिश्चित करेगी कि हमारे प्रोग्राम के पिछले भाग में ऑब्जेक्ट को सीरियल किए जाने पर भी एकमात्र उदाहरण वापस कर दिया जाएगा।
अंत में हमने थ्रेड्स और सीरियललाइज़ेशन के खिलाफ पर्याप्त सुरक्षा जोड़ी है लेकिन हमारा कोड भारी और बदसूरत लग रहा है। हमारे हीरो को एक ओवर देने की सुविधा देता है
public final class Foo implements Serializable {
private static final long serialVersionUID = 1L;
// Wrapped in a inner static class so that loaded only when required
private static class FooLoader {
// And no more fear of threads
private static final Foo INSTANCE = new Foo();
}
// TODO add private shouting construcor
public static Foo getInstance() {
return FooLoader.INSTANCE;
}
// Damn you serialization
@SuppressWarnings("unused")
private Foo readResolve() {
return FooLoader.INSTANCE;
}
}
हाँ यह हमारा वही नायक है :)
चूँकि लाइन private static final Foo INSTANCE = new Foo();
को केवल तब क्रियान्वित FooLoader
किया जाता है जब कक्षा वास्तव में उपयोग की जाती है, यह आलसी तात्कालिकता का ख्याल रखता है,
और क्या यह धागा सुरक्षित होने की गारंटी है।
और हम अब तक आए हैं, यहां हमने जो कुछ भी किया है उसे प्राप्त करने का सबसे अच्छा तरीका है
public enum Foo {
INSTANCE;
}
जिसे आंतरिक रूप से पसंद किया जाएगा
public class Foo {
// It will be our sole hero
private static final Foo INSTANCE = new Foo();
}
बस! क्रमबद्धता, धागे और बदसूरत कोड का कोई और डर नहीं। इसके अलावा ENUMS सिंगलटन को आलसी रूप से आरंभीकृत किया जाता है ।
यह दृष्टिकोण कार्यात्मक रूप से सार्वजनिक क्षेत्र के दृष्टिकोण के बराबर है, सिवाय इसके कि यह अधिक संक्षिप्त है, मुफ्त के लिए क्रमबद्धता मशीनरी प्रदान करता है, और परिष्कृत क्रमांकन या प्रतिबिंब हमलों के सामने भी, कई तात्कालिकता के खिलाफ एक विडंबना गारंटी प्रदान करता है। हालांकि इस दृष्टिकोण को व्यापक रूप से अपनाया जाना बाकी है, सिंगल-एलिमेंट एनम टाइप सिंगलटन को लागू करने का सबसे अच्छा तरीका है।
-जोशुआ बलोच "प्रभावी जावा" में
अब आप समझ गए होंगे कि क्यों ENUMS को सिंगलटन को लागू करने का सबसे अच्छा तरीका माना जाता है और आपके धैर्य के लिए धन्यवाद :)
मेरे ब्लॉग पर इसे अपडेट करें ।
serialVersionUID
है 0L
। तीसरी समस्या: कोई अनुकूलन नहीं: कोई भी वर्ग-विशेष लिखने वाला विशेषण, readObject, readObjectNoData, writeReplace, और enRes प्रकारों द्वारा परिभाषित readResolve तरीकों को क्रमबद्धता और deserialization के दौरान अनदेखा किया जाता है।
स्टु थॉम्पसन द्वारा पोस्ट किया गया समाधान Java5.0 और बाद में मान्य है। लेकिन मैं इसे उपयोग नहीं करना पसंद करूंगा क्योंकि मुझे लगता है कि यह त्रुटि प्रवण है।
वाष्पशील कथन को भूलना आसान है और यह समझना मुश्किल है कि यह क्यों आवश्यक है। अस्थिरता के बिना यह कोड डबल-चेक लॉकिंग एंटीपैटर्न के कारण थ्रेड सुरक्षित नहीं होगा। इसके बारे में अधिक देखें प्रैक्टिस में जावा कॉनएरेबिलिटी के पैरा 16.2.4 में । संक्षेप में: यह पैटर्न (Java5.0 से पहले या अस्थिर बयान के बिना) एक गलत स्थिति में बार ऑब्जेक्ट (जो अभी भी है) का संदर्भ दे सकता है।
यह पैटर्न प्रदर्शन अनुकूलन के लिए आविष्कार किया गया था। लेकिन यह वास्तव में अब कोई वास्तविक चिंता नहीं है। निम्नलिखित आलसी इनिशियलाइज़ेशन कोड तेज़ और -मोर महत्वपूर्ण है- पढ़ने में आसान।
class Bar {
private static class BarHolder {
public static Bar bar = new Bar();
}
public static Bar getBar() {
return BarHolder.bar;
}
}
getBar()
। (और अगर getBar
इसे "बहुत जल्दी" कहा जाता है, तो आपको एक ही समस्या का सामना करना पड़ेगा चाहे कोई एकल कैसे लागू हो।) आप ऊपर दिए गए कोड के आलसी वर्ग को यहां देख सकते हैं: pastebin.com/iq2eayiR
जावा 5+ में सुरक्षित धागा:
class Foo {
private static volatile Bar bar = null;
public static Bar getBar() {
if (bar == null) {
synchronized(Foo.class) {
if (bar == null)
bar = new Bar();
}
}
return bar;
}
}
संपादित करें : volatile
यहां संशोधक पर ध्यान दें। :) यह महत्वपूर्ण है क्योंकि इसके बिना, जेएमएम (जावा मेमोरी मॉडल) द्वारा इसके मूल्य में परिवर्तन देखने के लिए अन्य थ्रेड्स की गारंटी नहीं है। सिंक्रनाइज़ेशन का ध्यान नहीं है - यह केवल कोड के उस ब्लॉक तक पहुंच को क्रमबद्ध करता है।
EDIT 2 : @Bno का उत्तर विधेयक पुघ (FindBugs) द्वारा सुझाए गए दृष्टिकोण का विवरण है और यह बेहतर है। पढ़ो और उसके जवाब को भी वोट करो।
आलसी आरंभीकरण को भूल जाओ , यह बहुत समस्याग्रस्त है। यह सबसे सरल उपाय है:
public class A {
private static final A INSTANCE = new A();
private A() {}
public static A getInstance() {
return INSTANCE;
}
}
सुनिश्चित करें कि आपको वास्तव में इसकी आवश्यकता है। इसके खिलाफ कुछ तर्क देखने के लिए "सिंगलटन-पैटर्न" के लिए एक Google करें। इसमें कुछ भी गलत नहीं है क्योंकि मुझे लगता है लेकिन यह सिर्फ कुछ वैश्विक संसाधन / डेटा को उजागर करने के लिए एक तंत्र है इसलिए सुनिश्चित करें कि यह सबसे अच्छा तरीका है। विशेष रूप से मैंने निर्भरता इंजेक्शन को अधिक उपयोगी पाया है, खासकर यदि आप यूनिट परीक्षणों का उपयोग कर रहे हैं क्योंकि DI आपको परीक्षण उद्देश्यों के लिए नकली संसाधनों का उपयोग करने की अनुमति देता है।
मैं कुछ ऐसे जवाबों से विभूषित हूं, जो DI को सिंगलटन के उपयोग के विकल्प के रूप में सुझाते हैं; ये असंबंधित अवधारणाएँ हैं। आप किसी भी सिंगलटन या गैर-सिंगलटन (जैसे प्रति-धागा) उदाहरणों को इंजेक्ट करने के लिए DI का उपयोग कर सकते हैं। यदि आप स्प्रिंग 2.x का उपयोग करते हैं तो कम से कम यह सच है, मैं अन्य DI फ्रेमवर्क के लिए बात नहीं कर सकता।
तो ओपी के लिए मेरा जवाब होगा (सभी में सबसे तुच्छ नमूना कोड):
यह दृष्टिकोण आपको एक अच्छा डिकम्पोज्ड (और इसलिए लचीला और परीक्षण योग्य) आर्किटेक्चर देता है, जहां एक सिंगलटन का उपयोग करना एक आसानी से प्रतिवर्ती कार्यान्वयन विवरण है (बशर्ते आपके द्वारा उपयोग किया गया कोई भी सिंगलटन थ्रेडसेफ हो, निश्चित रूप से)।
TicketNumberer
जिस पर एक वैश्विक उदाहरण होना चाहिए, और जहां आप एक वर्ग लिखना चाहते हैं, TicketIssuer
जिसमें कोड की एक पंक्ति है, पर विचार करें int ticketNumber = ticketNumberer.nextTicketNumber();
। पारंपरिक सिंगलटन सोच में, कोड की पिछली पंक्ति कुछ इस तरह की होगी TicketNumberer ticketNumberer = TicketNumberer.INSTANCE;
। डीआई सोच में, कक्षा में एक निर्माता होगा public TicketIssuer(TicketNumberer ticketNumberer) { this.ticketNumberer = ticketNumberer; }
।
main
तरीका (या उसका एक मिनियन) निर्भरता पैदा करेगा और फिर कंस्ट्रक्टर को कॉल करेगा। अनिवार्य रूप से, एक वैश्विक चर (या एक वैश्विक विधि) का उपयोग केवल खतरनाक सेवा लोकेटर पैटर्न का एक सरल रूप है , और उस पैटर्न के किसी भी अन्य उपयोग की तरह, निर्भरता इंजेक्शन के साथ बदला जा सकता है।
वास्तव में विचार करें कि इसे लिखने से पहले आपको एक सिंगलटन की आवश्यकता क्यों है। उनका उपयोग करने के बारे में एक अर्ध-धार्मिक बहस है जिसे आप जावा में Google सिंगललेट्स पर आसानी से रोक सकते हैं।
व्यक्तिगत रूप से मैं कई कारणों से जितनी बार संभव हो एकल से बचने की कोशिश करता हूं, फिर से अधिकांश को एकल गायलेटगनों द्वारा पाया जा सकता है। मुझे लगता है कि अक्सर सिंगलेट्स का दुरुपयोग किया जाता है क्योंकि वे हर किसी के द्वारा समझने में आसान होते हैं, उनका उपयोग ओओ डिज़ाइन में "वैश्विक" डेटा प्राप्त करने के लिए एक तंत्र के रूप में किया जाता है और उनका उपयोग किया जाता है क्योंकि ऑब्जेक्ट जीवनचक्र प्रबंधन को दरकिनार करना आसान है (या वास्तव में यह सोचकर कि आप B के अंदर से A कैसे कर सकते हैं)। एक अच्छे मिडलग्राउंड के लिए इन्वर्ट ऑफ कंट्रोल (IoC) या डिपेंडेंसी इंजेक्शन (DI) जैसी चीजों को देखें।
यदि आपको वास्तव में एक की आवश्यकता है तो विकिपीडिया एक सिंगलटन के उचित कार्यान्वयन का एक अच्छा उदाहरण है।
निम्नलिखित 3 अलग-अलग दृष्टिकोण हैं
1) एनम
/**
* Singleton pattern example using Java Enumj
*/
public enum EasySingleton{
INSTANCE;
}
2) डबल जाँच ताला / आलसी लोड हो रहा है
/**
* Singleton pattern example with Double checked Locking
*/
public class DoubleCheckedLockingSingleton{
private static volatile DoubleCheckedLockingSingleton INSTANCE;
private DoubleCheckedLockingSingleton(){}
public static DoubleCheckedLockingSingleton getInstance(){
if(INSTANCE == null){
synchronized(DoubleCheckedLockingSingleton.class){
//double checking Singleton instance
if(INSTANCE == null){
INSTANCE = new DoubleCheckedLockingSingleton();
}
}
}
return INSTANCE;
}
}
3) स्थिर कारखाने विधि
/**
* Singleton pattern example with static factory method
*/
public class Singleton{
//initailzed during class loading
private static final Singleton INSTANCE = new Singleton();
//to prevent creating another instance of Singleton
private Singleton(){}
public static Singleton getSingleton(){
return INSTANCE;
}
}
मैं अपने सिंग्लेट्स को प्रबंधित करने के लिए स्प्रिंग फ्रेमवर्क का उपयोग करता हूं। यह वर्ग के "सिंगलटन-नेस" को लागू नहीं करता है (जो कि आप वास्तव में वैसे भी नहीं कर सकते हैं यदि कई वर्ग लोडर शामिल हैं), लेकिन विभिन्न प्रकार के ऑब्जेक्ट बनाने के लिए विभिन्न कारखानों को बनाने और कॉन्फ़िगर करने के लिए वास्तव में आसान तरीका प्रदान करता है।
संस्करण 1:
public class MySingleton {
private static MySingleton instance = null;
private MySingleton() {}
public static synchronized MySingleton getInstance() {
if(instance == null) {
instance = new MySingleton();
}
return instance;
}
}
आलसी लोडिंग, ब्लॉकिंग के साथ थ्रेड सुरक्षित, कम प्रदर्शन के कारण synchronized
।
संस्करण 2:
public class MySingleton {
private MySingleton() {}
private static class MySingletonHolder {
public final static MySingleton instance = new MySingleton();
}
public static MySingleton getInstance() {
return MySingletonHolder.instance;
}
}
आलसी लोडिंग, गैर-अवरोधक, उच्च प्रदर्शन के साथ थ्रेड सुरक्षित।
यदि आपको आलसी लोडिंग की आवश्यकता नहीं है, तो बस कोशिश करें
public class Singleton {
private final static Singleton INSTANCE = new Singleton();
private Singleton() {}
public static Singleton getInstance() { return Singleton.INSTANCE; }
protected Object clone() {
throw new CloneNotSupportedException();
}
}
यदि आप आलसी लोडिंग चाहते हैं और आप चाहते हैं कि आपका सिंगलटन थ्रेड-सुरक्षित हो, तो डबल-चेकिंग पैटर्न का प्रयास करें
public class Singleton {
private static Singleton instance = null;
private Singleton() {}
public static Singleton getInstance() {
if(null == instance) {
synchronized(Singleton.class) {
if(null == instance) {
instance = new Singleton();
}
}
}
return instance;
}
protected Object clone() {
throw new CloneNotSupportedException();
}
}
जैसा कि डबल चेकिंग पैटर्न को काम करने की गारंटी नहीं है (कंपाइलर्स के साथ कुछ मुद्दे के कारण, मुझे इसके बारे में अधिक कुछ भी नहीं पता है।), आप पूरे getInstance-method को सिंक्रनाइज़ करने या अपने सभी सिंगलेट्स के लिए एक रजिस्ट्री बनाने का भी प्रयास कर सकते हैं।
volatile
मैं Enum सिंगलटन कहूँगा
जावा में एनम का उपयोग करने वाला सिंगलटन आमतौर पर एनम सिंगलटन घोषित करने का तरीका है। Enum singleton में आवृत्ति चर और आवृत्ति विधि हो सकती है। सादगी की खातिर, यह भी ध्यान दें कि यदि आप किसी भी उदाहरण विधि का उपयोग कर रहे हैं तो आपको उस विधि की थ्रेड सुरक्षा सुनिश्चित करने की आवश्यकता है यदि यह सभी वस्तु की स्थिति को प्रभावित करता है।
एक एनम का उपयोग लागू करना बहुत आसान है और इसमें सीरियल करने योग्य वस्तुओं के बारे में कोई कमियां नहीं हैं, जिन्हें अन्य तरीकों से दरकिनार करना है।
/**
* Singleton pattern example using Java Enum
*/
public enum Singleton {
INSTANCE;
public void execute (String arg) {
//perform operation here
}
}
आप सिंगलटन पर Singleton.INSTANCE
कॉलिंग getInstance()
विधि की तुलना में इसे बहुत आसान बना सकते हैं ।
1.12 Enum Constants का Serialization
Enum constants को साधारण serializable या बाहरी वस्तुओं की तुलना में अलग तरीके से क्रमबद्ध किया जाता है। एक एनम स्थिरांक के क्रमबद्ध रूप में केवल इसका नाम होता है; स्थिरांक के क्षेत्र मान रूप में मौजूद नहीं हैं। एक एनम स्थिरांक को क्रमबद्ध करने के लिए,
ObjectOutputStream
एनम निरंतर के नाम विधि द्वारा लौटाया गया मान लिखते हैं। एक एनम स्थिरांक को डिस्क्राइब करने के लिए,ObjectInputStream
स्ट्रीम से निरंतर नाम पढ़ता है; deserialized स्थिरांक तबjava.lang.Enum.valueOf
विधि को कॉल करके प्राप्त किया जाता है , निरंतर स्थिर नाम को तर्क के रूप में प्राप्त निरंतर नाम के साथ गुजरता है। अन्य अनुक्रमिक या बाहरी वस्तुओं की तरह, एनरम स्थिरांक क्रमिक धारा में बाद में दिखाई देने वाले बैक रेफरेंस के लक्ष्य के रूप में कार्य कर सकते हैं।प्रक्रिया है जिसके द्वारा enum स्थिरांक क्रमानुसार लगे हुए हैं अनुकूलित नहीं किया जा सकता: किसी भी वर्ग-विशेष
writeObject
,readObject
,readObjectNoData
,writeReplace
, औरreadResolve
तरीकों enum प्रकार से परिभाषित किया गया क्रमबद्धता और अक्रमांकन दौरान अनदेखी कर रहे हैं। इसी तरह, किसी भीserialPersistentFields
याserialVersionUID
क्षेत्र की घोषणाओं को भी नजरअंदाज कर दिया जाता है - सभी enum प्रकारों का एक निश्चितserialVersionUID
होता है0L
। सीरम प्रकारों के लिए दस्तावेजीकरण फ़ील्ड और डेटा का दस्तावेज़ अनावश्यक है, क्योंकि भेजे गए डेटा के प्रकार में कोई भिन्नता नहीं है।
पारंपरिक सिंग्लेटन्स के साथ एक और समस्या यह है कि एक बार जब आप Serializable
इंटरफ़ेस लागू करते हैं , तो वे अब सिंगलटन नहीं रह जाते हैं क्योंकि readObject()
विधि हमेशा जावा में कंस्ट्रक्टर की तरह एक नया उदाहरण लौटाती है। readResolve()
नीचे दिए गए सिंगलटन के साथ बदलकर नए बनाए गए उदाहरण का उपयोग करके और इसे त्यागने से बचा जा सकता है
// readResolve to prevent another instance of Singleton
private Object readResolve(){
return INSTANCE;
}
यह और भी जटिल हो सकता है यदि आपका सिंगलटन क्लास राज्य बनाए रखता है, जैसा कि आपको उन्हें क्षणिक बनाने की आवश्यकता है, लेकिन एनम सिंगलटन के साथ, सीरियल को जेवीएम द्वारा गारंटी दी जाती है।
अच्छा पढ़ा
There are 4 ways to create a singleton in java.
1- eager initialization singleton
public class Test{
private static final Test test = new Test();
private Test(){}
public static Test getTest(){
return test;
}
}
2- lazy initialization singleton (thread safe)
public class Test {
private static volatile Test test;
private Test(){}
public static Test getTest() {
if(test == null) {
synchronized(Test.class) {
if(test == null){test = new Test();
}
}
}
return test;
}
3- Bill Pugh Singleton with Holder Pattern (Preferably the best one)
public class Test {
private Test(){}
private static class TestHolder{
private static final Test test = new Test();
}
public static Test getInstance(){
return TestHolder.test;
}
}
4- enum singleton
public enum MySingleton {
INSTANCE;
private MySingleton() {
System.out.println("Here");
}
}
इस पर खेल में थोड़ी देर हो सकती है, लेकिन एक सिंगलटन को लागू करने के आसपास बहुत सारी बारीकियां हैं। धारक पैटर्न का उपयोग कई स्थितियों में नहीं किया जा सकता है। और वाष्पशील का उपयोग करते समय IMO - आपको एक स्थानीय चर का उपयोग करना चाहिए। आइए शुरुआत में शुरू करते हैं और समस्या पर पुनरावृति करते हैं। आप देखेंगे कि मेरा क्या मतलब है।
पहला प्रयास कुछ इस तरह दिख सकता है:
public class MySingleton {
private static MySingleton INSTANCE;
public static MySingleton getInstance() {
if (INSTANCE == null) {
INSTANCE = new MySingleton();
}
return INSTANCE;
}
...
}
यहां हमारे पास MySingleton वर्ग है जिसमें एक निजी स्थिर सदस्य है जिसे INSTANCE कहा जाता है, और एक सार्वजनिक स्थैतिक विधि जिसे getInstance () कहा जाता है। पहली बार getInstance () कहा जाता है, INSTANCE सदस्य शून्य है। तब प्रवाह निर्माण की स्थिति में आ जाएगा और MySingleton वर्ग का एक नया उदाहरण तैयार करेगा। इसके बाद कॉल करने के लिए getInstance () यह पाएगा कि INSTANCE वैरिएबल पहले से सेट है, और इसलिए एक और MySletonleton उदाहरण न बनाएं। यह सुनिश्चित करता है कि MySingleton का केवल एक उदाहरण है जो getInstance () के सभी कॉलर्स के बीच साझा किया गया है।
लेकिन इस कार्यान्वयन में एक समस्या है। बहु-थ्रेडेड अनुप्रयोगों में एकल उदाहरण के निर्माण पर एक दौड़ की स्थिति होगी। यदि निष्पादन के कई थ्रेड्स एक ही समय में getInstance () विधि (या उसके आसपास) से टकराते हैं, तो वे प्रत्येक INSTANCE सदस्य को अशक्त के रूप में देखेंगे। इसके परिणामस्वरूप प्रत्येक थ्रेड एक नया MySingleton उदाहरण बनाता है और बाद में INSTANCE सदस्य को सेट करता है।
private static MySingleton INSTANCE;
public static synchronized MySingleton getInstance() {
if (INSTANCE == null) {
INSTANCE = new MySingleton();
}
return INSTANCE;
}
यहां हमने getInstance () विधि को सिंक्रनाइज़ करने के लिए विधि हस्ताक्षर में सिंक्रनाइज़ कीवर्ड का उपयोग किया है। यह निश्चित रूप से हमारी दौड़ की स्थिति को ठीक करेगा। थ्रेड्स अब ब्लॉक हो जाएंगे और एक समय में विधि एक में प्रवेश करेंगे। लेकिन यह एक प्रदर्शन समस्या भी पैदा करता है। न केवल यह कार्यान्वयन एकल उदाहरण के निर्माण को सिंक्रनाइज़ करता है, यह रीड्स सहित, getInstance () के लिए सभी कॉल को सिंक्रनाइज़ करता है। रीड्स को सिंक्रनाइज़ करने की आवश्यकता नहीं है क्योंकि वे बस INSTANCE का मूल्य वापस करते हैं। चूँकि रीड हमारे कॉल का बड़ा हिस्सा बन जाएगा (याद रखें, तात्कालिकता केवल पहली कॉल पर होती है), हम पूरी विधि को सिंक्रनाइज़ करके एक अनावश्यक प्रदर्शन को प्रभावित करेंगे।
private static MySingleton INSTANCE;
public static MySingleton getInstance() {
if (INSTANCE == null) {
synchronize(MySingleton.class) {
INSTANCE = new MySingleton();
}
}
return INSTANCE;
}
यहां हमने विधि हस्ताक्षर से सिंक्रोनाइज़ेशन स्थानांतरित किया है, एक सिंक्रनाइज़ेशन ब्लॉक में जो MySingleton उदाहरण के निर्माण को लपेटता है। लेकिन क्या इससे हमारी समस्या हल हो जाती है? खैर, हम अब रीड्स पर रोक नहीं लगा रहे हैं, लेकिन हमने एक कदम भी पीछे ले लिया है। एकाधिक धागे एक ही समय में या उसके आसपास getInstance () विधि को हिट करेंगे और वे सभी INSTANCE सदस्य को शून्य के रूप में देखेंगे। वे तब सिंक्रनाइज़ किए गए ब्लॉक को मारेंगे जहां एक लॉक प्राप्त करेगा और उदाहरण बना देगा। जब वह थ्रेड ब्लॉक से बाहर निकलता है, तो अन्य थ्रेड लॉक के लिए चुनाव करेंगे, और एक-एक करके प्रत्येक थ्रेड ब्लॉक के माध्यम से गिर जाएगा और हमारी कक्षा का एक नया उदाहरण बनाएगा। इसलिए हम वहीं हैं जहां हमने शुरुआत की थी।
private static MySingleton INSTANCE;
public static MySingleton getInstance() {
if (INSTANCE == null) {
synchronized(MySingleton.class) {
if (INSTANCE == null) {
INSTANCE = createInstance();
}
}
}
return INSTANCE;
}
यहां हम INSIDE ब्लॉक से एक और चेक जारी करते हैं। यदि INSTANCE सदस्य पहले ही सेट कर दिया गया है, तो हम प्रारंभ को छोड़ देंगे। इसे डबल-चेकिंग लॉकिंग कहा जाता है।
यह हमारी कई तात्कालिकता की समस्या को हल करता है। लेकिन एक बार फिर, हमारे समाधान ने एक और चुनौती पेश की है। अन्य सूत्र शायद "देख" नहीं सकते हैं कि INSTANCE सदस्य को अपडेट किया गया है। यह इस कारण से है कि जावा मेमोरी ऑपरेशन को कैसे अनुकूलित करता है। थ्रेड्स मुख्य मेमोरी से चर के मूल मूल्यों को सीपीयू के कैश में कॉपी करते हैं। मानों में परिवर्तन तब उस कैश में लिखे और पढ़े जाते हैं। यह जावा की एक विशेषता है जिसे प्रदर्शन को अनुकूलित करने के लिए डिज़ाइन किया गया है। लेकिन यह हमारे सिंगलटन कार्यान्वयन के लिए एक समस्या पैदा करता है। एक दूसरा धागा - एक अलग सीपीयू या कोर द्वारा संसाधित किया जा रहा है, एक अलग कैश का उपयोग करके - पहले द्वारा किए गए परिवर्तनों को नहीं देखेगा। यह दूसरे धागे के कारण INSTANCE के सदस्य को हमारे सिंगल्सटन के एक नए उदाहरण के लिए मजबूर करने के रूप में देखने के लिए पैदा करेगा।
private static volatile MySingleton INSTANCE;
public static MySingleton getInstance() {
if (INSTANCE == null) {
synchronized(MySingleton.class) {
if (INSTANCE == null) {
INSTANCE = createInstance();
}
}
}
return INSTANCE;
}
हम इसे INSTANCE सदस्य की घोषणा पर अस्थिर कीवर्ड का उपयोग करके हल करते हैं। यह संकलक को हमेशा पढ़ने, और मुख्य मेमोरी, और सीपीयू कैश से लिखने के लिए बताएगा।
लेकिन यह साधारण बदलाव एक कीमत पर आता है। क्योंकि हम सीपीयू कैश को दरकिनार कर रहे हैं, हम प्रत्येक बार जब हम अस्थिर इंस्टेंस सदस्य पर काम करते हैं, तो एक प्रदर्शन करेंगे - जो हम 4 बार करते हैं। हम अस्तित्व (1 और 2) को दोहराते हैं, मान (3) सेट करते हैं, और फिर मान (4) वापस करते हैं। कोई यह तर्क दे सकता है कि यह रास्ता फ्रिंज केस है क्योंकि हम केवल विधि के पहले कॉल के दौरान ही उदाहरण बनाते हैं। शायद निर्माण पर हिट एक प्रदर्शन सहनीय है। लेकिन यहां तक कि हमारा मुख्य उपयोग-मामला, वाष्पशील सदस्य पर दो बार काम करेगा। एक बार अस्तित्व की जांच करने के लिए, और फिर से इसके मूल्य को वापस करने के लिए।
private static volatile MySingleton INSTANCE;
public static MySingleton getInstance() {
MySingleton result = INSTANCE;
if (result == null) {
synchronized(MySingleton.class) {
result = INSTANCE;
if (result == null) {
INSTANCE = result = createInstance();
}
}
}
return result;
}
चूँकि प्रदर्शन हिट वाष्पशील सदस्य पर सीधे काम करने के कारण होता है, आइए एक स्थानीय चर को अस्थिर के मान पर सेट करें और इसके बजाय स्थानीय चर पर काम करें। यह अस्थिरता पर हमारे द्वारा संचालित होने की संख्या को कम कर देगा, जिससे हमारे कुछ खोए हुए प्रदर्शन को पुनः प्राप्त होगा। ध्यान दें कि हमें अपने स्थानीय चर को फिर से सेट करना होगा जब हम सिंक्रनाइज़ किए गए ब्लॉक में प्रवेश करेंगे। यह सुनिश्चित करता है कि यह किसी भी परिवर्तन के साथ अद्यतित हो, जबकि हम लॉक का इंतजार कर रहे थे।
मैंने हाल ही में इस बारे में एक लेख लिखा था। सिंगलाटन का पुनर्निर्माण । आप इन उदाहरणों के बारे में अधिक जानकारी और वहां "धारक" पैटर्न का एक उदाहरण पा सकते हैं। डबल-चेक किए गए अस्थिर दृष्टिकोण को दर्शाने वाला एक वास्तविक-विश्व उदाहरण भी है। उम्मीद है की यह मदद करेगा।
class MySingleton
- शायद यह होना चाहिए final
?
BearerToken
उदाहरण स्थिर नहीं है क्योंकि यह उसी का हिस्सा है BearerTokenFactory
- जिसे एक विशिष्ट ऑथराइजेशन सर्वर से कॉन्फ़िगर किया गया है। कई BearerTokenFactory
वस्तुएं हो सकती हैं - हर एक का अपना "कैश" है BearerToken
जो इसे समाप्त होने तक सौंपता है। hasExpired()
पर विधि BeraerToken
कारखाने के में कहा जाता है get()
विधि सुनिश्चित करने के लिए इसे बाहर हाथ नहीं है एक टोकन की समयावधि समाप्त। यदि समय सीमा समाप्त हो जाती है, तो एक नया टोकन अनुरोध किया जाएगा। कोड ब्लॉक के बाद का पैराग्राफ इसे अधिक विस्तार से बताता है।
यह एक सरल लागू करने का तरीका है singleton
:
public class Singleton {
// It must be static and final to prevent later modification
private static final Singleton INSTANCE = new Singleton();
/** The constructor must be private to prevent external instantiation */
private Singleton(){}
/** The public static method allowing to get the instance */
public static Singleton getInstance() {
return INSTANCE;
}
}
यह है कि कैसे ठीक से आलसी अपने बनाने के लिए singleton
:
public class Singleton {
// The constructor must be private to prevent external instantiation
private Singleton(){}
/** The public static method allowing to get the instance */
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
/**
* The static inner class responsible for creating your instance only on demand,
* because the static fields of a class are only initialized when the class
* is explicitly called and a class initialization is synchronized such that only
* one thread can perform it, this rule is also applicable to inner static class
* So here INSTANCE will be created only when SingletonHolder.INSTANCE
* will be called
*/
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
}
getInstance()
। लेकिन वास्तव में अगर आपके पास अपनी कक्षा में कोई अन्य स्थिर पद्धति नहीं है Singleton
और आप केवल कॉल करते हैं getInstance()
तो कोई वास्तविक अंतर नहीं है।
आप की जरूरत की दोबारा जाँच मुहावरा अगर आप lazily एक वर्ग के उदाहरण चर लोड करने के लिए की जरूरत है। यदि आपको स्टैटिक वैरिएबल या सिंगलटन लेज़ीली लोड करने की आवश्यकता है, तो आपको डिमांड होल्डर मुहावरे पर इनिटलाइजेशन की आवश्यकता है ।
इसके अलावा, अगर सिंगलटन को सेरिलियाज़बल होने की आवश्यकता होती है, तो अन्य सभी क्षेत्रों को क्षणिक होने की आवश्यकता होती है और सिंग्लटन ऑब्जेक्ट को अपरिवर्तित बनाए रखने के लिए readResolve () विधि को लागू करने की आवश्यकता होती है। अन्यथा, हर बार जब ऑब्जेक्ट डिसेररलाइज़ किया जाता है, तो ऑब्जेक्ट का एक नया उदाहरण बनाया जाएगा। क्या readResolve () करता है, readObject () द्वारा पढ़ी गई नई ऑब्जेक्ट को प्रतिस्थापित करता है, जिसने यह सुनिश्चित किया कि नई वस्तु को कचरा एकत्र किया जाए क्योंकि इसके बारे में कोई चर नहीं है।
public static final INSTANCE == ....
private Object readResolve() {
return INSTANCE; // original singleton instance.
}
सिंगलटन ऑब्जेक्ट बनाने के विभिन्न तरीके:
यहोशू बलोच के अनुसार - एनम सबसे अच्छा होगा।
आप डबल चेक लॉकिंग का भी उपयोग कर सकते हैं।
यहां तक कि आंतरिक स्थिर वर्ग का उपयोग किया जा सकता है।
एनम सिंगलटन
एक सिंगलटन को लागू करने का सबसे सरल तरीका जो थ्रेड-सुरक्षित है, एक एनम का उपयोग कर रहा है
public enum SingletonEnum {
INSTANCE;
public void doSomething(){
System.out.println("This is a singleton");
}
}
यह कोड जावा 1.5 में Enum की शुरुआत के बाद से काम करता है
डबल चेकिंग लॉकिंग
यदि आप एक "क्लासिक" सिंगलटन को कोडित करना चाहते हैं जो एक बहुस्तरीय वातावरण में काम करता है (जावा 1.5 से शुरू होता है) तो आपको इस का उपयोग करना चाहिए।
public class Singleton {
private static volatile Singleton instance = null;
private Singleton() {
}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class){
if (instance == null) {
instance = new Singleton();
}
}
}
return instance ;
}
}
यह 1.5 से पहले थ्रेड-सुरक्षित नहीं है क्योंकि अस्थिर कीवर्ड का कार्यान्वयन अलग था।
प्रारंभिक लोडिंग सिंगलटन (जावा 1.5 से पहले भी काम करता है)
जब क्लास लोड होती है और थ्रेड सुरक्षा प्रदान करता है, तो यह कार्यान्वयन सिंगलटन को त्वरित करता है।
public class Singleton {
private static final Singleton instance = new Singleton();
private Singleton() {
}
public static Singleton getInstance() {
return instance;
}
public void doSomething(){
System.out.println("This is a singleton");
}
}
एक और तर्क जो अक्सर सिंगलटन के खिलाफ इस्तेमाल किया जाता है, वह है उनकी परीक्षण क्षमता की समस्याएं। परीक्षण के उद्देश्यों के लिए सिंगलटन आसानी से नकली नहीं होते हैं। यदि यह एक समस्या बनती है, तो मुझे निम्न मामूली संशोधन करना पसंद है:
public class SingletonImpl {
private static SingletonImpl instance;
public static SingletonImpl getInstance() {
if (instance == null) {
instance = new SingletonImpl();
}
return instance;
}
public static void setInstance(SingletonImpl impl) {
instance = impl;
}
public void a() {
System.out.println("Default Method");
}
}
जोड़ा setInstance
विधि परीक्षण के दौरान सिंगलटन वर्ग के एक mockup कार्यान्वयन की स्थापना की अनुमति देता है:
public class SingletonMock extends SingletonImpl {
@Override
public void a() {
System.out.println("Mock Method");
}
}
यह प्रारंभिक आरंभीकरण दृष्टिकोण के साथ भी काम करता है:
public class SingletonImpl {
private static final SingletonImpl instance = new SingletonImpl();
private static SingletonImpl alt;
public static void setInstance(SingletonImpl inst) {
alt = inst;
}
public static SingletonImpl getInstance() {
if (alt != null) {
return alt;
}
return instance;
}
public void a() {
System.out.println("Default Method");
}
}
public class SingletonMock extends SingletonImpl {
@Override
public void a() {
System.out.println("Mock Method");
}
}
यह इस कार्यक्षमता को सामान्य अनुप्रयोग के लिए भी उजागर करने का दोष है। उस कोड पर काम करने वाले अन्य डेवलपर्स को एक विशिष्ट फ़ंक्शन को बदलने और इस प्रकार पूरे एप्लिकेशन व्यवहार को बदलने के लिए codesetInstance to विधि का उपयोग करने के लिए लुभाया जा सकता है, इसलिए इस विधि में कम से कम एक अच्छी चेतावनी इसमें शामिल होनी चाहिए।
फिर भी, मॉकअप-टेस्टिंग (जब जरूरत हो) की संभावना के लिए, इस कोड एक्सपोज़र को भुगतान करने के लिए स्वीकार्य मूल्य हो सकता है।
सबसे सरल एकल वर्ग
public class Singleton {
private static Singleton singleInstance = new Singleton();
private Singleton() {}
public static Singleton getSingleInstance() {
return singleInstance;
}
}
मुझे अभी भी लगता है कि java 1.5 के बाद, enum सबसे अच्छा उपलब्ध सिंगलटन कार्यान्वयन है क्योंकि यह यह भी सुनिश्चित करता है कि बहु थ्रेडेड वातावरण में भी - केवल एक ही उदाहरण बनता है।
public enum Singleton{
INSTANCE;
}
और आप कर रहे हैं !!!
इस पोस्ट पर एक नजर।
जावा के मुख्य पुस्तकालयों में जीओएफ डिज़ाइन पैटर्न के उदाहरण
सर्वश्रेष्ठ उत्तर के "सिंगलटन" अनुभाग से,
सिंगलटन (समान रूप से स्वयं को लौटाने वाली रचनात्मक विधियों द्वारा पहचाने जाने योग्य)
- java.lang.Runtime # getRuntime ()
- java.awt.Desktop # getDesktop ()
- java.lang.System # getSecurityManager ()
आप स्वयं जावा मूल वर्गों से सिंग्लटन का उदाहरण भी सीख सकते हैं।
मैंने कभी देखा है सबसे अच्छा सिंगलटन पैटर्न आपूर्तिकर्ता इंटरफ़ेस का उपयोग करता है।
निचे देखो:
public class Singleton<T> implements Supplier<T> {
private boolean initialized;
private Supplier<T> singletonSupplier;
public Singleton(T singletonValue) {
this.singletonSupplier = () -> singletonValue;
}
public Singleton(Supplier<T> supplier) {
this.singletonSupplier = () -> {
// The initial supplier is temporary; it will be replaced after initialization
synchronized (supplier) {
if (!initialized) {
T singletonValue = supplier.get();
// Now that the singleton value has been initialized,
// replace the blocking supplier with a non-blocking supplier
singletonSupplier = () -> singletonValue;
initialized = true;
}
return singletonSupplier.get();
}
};
}
@Override
public T get() {
return singletonSupplier.get();
}
}
कभी-कभी एक सरल " static Foo foo = new Foo();
" पर्याप्त नहीं होता है। बस कुछ बुनियादी डेटा प्रविष्टि के बारे में सोचें जो आप करना चाहते हैं।
दूसरी ओर आपको किसी भी तरीके को सिंक्रनाइज़ करना होगा जो सिंगलटन वैरिएबल को इस तरह से इंस्टेंट करता है। तुल्यकालन इस तरह के रूप में खराब नहीं है, लेकिन यह प्रदर्शन के मुद्दों या लॉकिंग को जन्म दे सकता है (इस उदाहरण का उपयोग करके बहुत ही दुर्लभ स्थितियों में। यह है)
public class Singleton {
private static Singleton instance = null;
static {
instance = new Singleton();
// do some of your instantiation stuff here
}
private Singleton() {
if(instance!=null) {
throw new ErrorYouWant("Singleton double-instantiation, should never happen!");
}
}
public static getSingleton() {
return instance;
}
}
अब क्या होता है? वर्ग को लोडर के माध्यम से लोड किया जाता है। कक्षा को बाइट ऐरे से व्याख्यायित करने के बाद, VM स्थिर {} - ब्लॉक को निष्पादित करता है । यह पूरा रहस्य है: स्थैतिक-ब्लॉक को केवल एक बार कहा जाता है, दिए गए पैकेज के दिए गए वर्ग (नाम) को इस एक वर्ग लोडर द्वारा लोड किया जाता है।
public class Singleton {
private static final Singleton INSTANCE = new Singleton();
private Singleton(){
if (INSTANCE != null)
throw new IllegalStateException (“Already instantiated...”);
}
public synchronized static Singleton getInstance() {
return INSTANCE;
}
}
जैसा कि हमने getInstance से पहले सिंक्रोनाइज़्ड कीवर्ड को जोड़ दिया है, हमने उस स्थिति में दौड़ की स्थिति से बचा लिया है जब दो धागे एक ही समय में getInstance को कॉल करते हैं।