9

मेरे साक्षात्कार के दौरान, साक्षात्कारकर्ता ने सिंगलटन पैटर्न के साथ अपना प्रश्न शुरू किया। मैंने नीचे लिखा था। फिर, उसने से पूछा कि क्या हमें getInstance विधि के अंदर नलिटी की जांच नहीं करनी चाहिए?मल्टी थ्रेडेड वातावरण में सिंगलटन पैटर्न

मैंने जवाब दिया, यह आवश्यक नहीं है, क्योंकि सदस्य स्थिर प्रकार है और उसी समय आरंभ किया जा रहा है। लेकिन, ऐसा लगता है कि वह मेरे जवाब से संतुष्ट नहीं था। क्या मैं सही हूं या नहीं?

class Single { 

     private final static Single sing = new Single();  
     private Single() { 
     }   
     public static Single getInstance() { 
      return sing; 
     } 
    } 

अब, अगला प्रश्न वह बहु-थ्रेडेड वातावरण के लिए सिंगलटन कक्षा लिखने के लिए कहता है। फिर, मैंने डबल चेक सिंगलटन कक्षा लिखी।

class MultithreadedSingle {   
     private static MultithreadedSingle single;  
     private MultithreadedSingle() { 
     }   
     public static MultithreadedSingle getInstance() { 
      if(single==null){ 
        synchronized(MultithreadedSingle.class){ 
         if(single==null){ 
          single= new MultithreadedSingle(); 
           }  
         } 
        } 
      return single; 
     } 
    } 

फिर, वह synchronized और पुनः जाँच का उपयोग कर के समक्ष आपत्ति की थी और कहा यह बेकार है। आप दो बार क्यों जांच रहे हैं और आप सिंक्रनाइज़ क्यों कर रहे हैं? मैंने उसे कई परिदृश्यों से मनाने की कोशिश की। लेकिन, उसने नहीं किया।

बाद में, घर पर मैंने कोड के नीचे कोशिश की जहां मैं एकाधिक थ्रेड के साथ सरल सिंगलटन कक्षा का उपयोग कर रहा हूं।

Thread-0 : 1153093538 
Thread-0 : 1153093538 
Thread-0 : 1153093538 
Thread-0 : 1153093538 
Thread-0 : 1153093538 
Thread-4 : 1153093538 
Thread-1 : 1153093538 
Thread-2 : 1153093538 
Thread-3 : 1153093538 
Thread-3 : 1153093538 
Thread-3 : 1153093538 
Thread-3 : 1153093538 
Thread-3 : 1153093538 
Thread-2 : 1153093538 
Thread-2 : 1153093538 
Thread-2 : 1153093538 
Thread-2 : 1153093538 
Thread-1 : 1153093538 
Thread-1 : 1153093538 
Thread-1 : 1153093538 
Thread-1 : 1153093538 
Thread-4 : 1153093538 
Thread-4 : 1153093538 
Thread-4 : 1153093538 
Thread-4 : 1153093538 

तो, सवाल है, यह आवश्यक या/और में दोहरी जांच विधि मल्टी-थ्रेडेड वातावरण synchronize उपयोग करने के लिए है:

public class Test { 

    public static void main(String ar[]) { 
     Test1 t = new Test1(); 
     Test1 t2 = new Test1(); 
     Test1 t3 = new Test1(); 
     Thread tt = new Thread(t); 
     Thread tt2 = new Thread(t2); 
     Thread tt3 = new Thread(t3); 
     Thread tt4 = new Thread(t); 
     Thread tt5 = new Thread(t); 
     tt.start(); 
     tt2.start(); 
     tt3.start(); 
     tt4.start(); 
     tt5.start(); 

    } 
} 

final class Test1 implements Runnable { 

    @Override 
    public void run() { 
     for (int i = 0; i < 5; i++) { 
      System.out.println(Thread.currentThread().getName() + " : " + Single.getInstance().hashCode()); 
     } 
    } 

} 
    class Single { 

     private final static Single sing = new Single();  
     private Single() { 
     }   
     public static Single getInstance() { 
      return sing; 
     } 
    } 

नीचे उत्पादन है? यह मेरे पहले कोड की तरह लगता है (कोड की कोई अतिरिक्त लाइन जोड़ने के बिना) दोनों प्रश्नों का उत्तर था। किसी भी सुधार और ज्ञान शेयर की सराहना की जाएगी।

+0

केवल आपत्ति मैं पहली बार उदाहरण के लिए है छोटे अक्षर "एस" कक्षा में "एकल है का उपयोग करते हुए ";) मुझे कोई दौड़ की स्थिति नहीं दिख रही है। या तो कक्षा में, या किसी भी संभावित ग्राहक में जो "सिंगल.getInstance()", – paulsm4

+0

@ paulsm4 को कॉल कर सकता है, सभी को सही कर सकता है;) यदि आप अभी भी किसी भी चूक गए हैं, तो आप संपादित कर सकते हैं :) – Ravi

+0

कोई "सुधार" वास्तव में आवश्यक नहीं है: लेकिन " ऊपरी मामला "निश्चित रूप से अच्छी शैली है। आपके प्रश्न के अनुसार: मैं पूरी तरह से यशावित और इव्गेनी डोरोफिव के साथ सहमत हूं। कृपया दोनों को ऊपर उठाने के लिए स्वतंत्र महसूस करें। और यदि आप भी सहमत हैं तो "स्वीकार करें"। – paulsm4

उत्तर

5

आपका पहला उदाहरण बिल्कुल सही है, और आमतौर पर सिंगलेट के लिए पसंदीदा "मुहावरे" होता है। एक दूसरे को एक एकल तत्व enum बनाना है:

public enum Single { 
    INSTANCE; 

    ... 
} 

दो दृष्टिकोण बहुत समान है जब तक कि वर्ग Serializable है कर रहे हैं, जो मामले में enum दृष्टिकोण बहुत सही पाने के लिए आसान है - लेकिन अगर वर्ग प्रतिसाद नहीं 'Serializable, मैं वास्तव में एक स्टाइलिस्ट मामले के रूप में, अपने दृष्टिकोण enum पसंद करते हैं। इंटरफ़ेस को लागू करने या कक्षा को विस्तारित करने के कारण "गलती से" Serializable बनने के लिए देखें जो स्वयं ही Serializable है।

आप डबल-चेक किए गए लॉक उदाहरण में शून्यता के लिए दूसरी जांच के बारे में भी सही हैं। हालांकि, जावा में काम करने के लिए sing फ़ील्ड volatile होना चाहिए; अन्यथा, एक थ्रेड लेखन के बीच sing पर कोई औपचारिक "होता-पहले" किनारा नहीं है और इसे पढ़ने के लिए एक और धागा है। इसके परिणामस्वरूप null को देखकर दूसरा थ्रेड हो सकता है, भले ही वेरिएबल को सौंपा गया पहला थ्रेड, या sing इंस्टेंस में राज्य है, तो इसके परिणामस्वरूप उस दूसरे थ्रेड में केवल कुछ राज्य देख सकते हैं (आंशिक रूप से निर्मित ऑब्जेक्ट देखकर) ।

+0

लेकिन दूसरे परिदृश्य के लिए, मैंने 'सिंक्रनाइज़' का उपयोग नहीं किया है सहायक मान लीजिए कि दो थ्रेड पास पहली नलिटी है, लेकिन सिग्नलनाइज्ड 'के अंदर केवल थ्रेड की अनुमति होगी और वहां हम फिर से जांच कर रहे हैं कि प्रारंभिकता से पहले शून्यता है या नहीं। इसके अलावा, हमें बहु-थ्रेडेड वातावरण के लिए' एनम 'या किसी अन्य विधि का उपयोग क्यों करना चाहिए। अगर हम सरल उदाहरण से प्राप्त कर सकते हैं? : – Ravi

+0

सिंक्रनाइज़ पर्याप्त नहीं है, नहीं। यह एक डेटा रेस है (दोनों फ़ील्ड द्वारा उसी फ़ील्ड तक पहुंच और बिना किसी घटना के पढ़ता है), और रीजनिंग के कारण पागल सामान के सभी प्रकार हो सकते हैं। कंपाइलर और सीपीयू प्रदर्शन को अनुकूलित करने के लिए निर्देशों को पुन: व्यवस्थित कर सकते हैं, और जब आप डेटा रेस फेंकते हैं, चीजें बहुत जटिल और सूक्ष्म हो जाती हैं। यह अभी भी सही होने के लिए _possible_ है ... लेकिन सबसे आसान बात यह है कि फ़ील्ड 'अस्थिर' को चिह्नित करें। – yshavit

+2

enum दृष्टिकोण के लिए, यह ज्यादातर स्टाइलिस्ट है। धारावाहिकता समस्या तब आती है जब कोई आपके सिंगलटन को क्रमबद्ध करता है और फिर इसे deserializes - अब दो उदाहरण हैं, और यह एक सिंगलटन नहीं है! फिर, इसके खिलाफ सुरक्षा करने के तरीके हैं, लेकिन यहां तक ​​कि यह थोड़ा मुश्किल हो सकता है; सबसे आसान बात यह है कि इसे एक enum चिह्नित करें और JVM को आपके लिए हल करने के क्रमबद्ध क्रमबद्धता उदाहरण दें। – yshavit

1

आपका पहला उत्तर मेरे लिए अच्छा प्रतीत होता है, क्योंकि दौड़ की स्थिति का कोई मौका नहीं है।

ज्ञान साझा करने के लिए, जावा में एक सिंगलटन को लागू करने का सबसे अच्छा तरीका Enum का उपयोग कर रहा है। बिल्कुल एक उदाहरण के साथ एक enum बनाएँ, और यह है।

public enum MyEnum { 
    INSTANCE; 

    // your other methods 
} 

अच्छी किताब Effective Java से - -

[....] यह दृष्टिकोण कार्यात्मक सार्वजनिक क्षेत्र दृष्टिकोण के बराबर है, सिवाय इसके कि यह बहुत अधिक संक्षिप्त है, है प्रदान करता है कोड नमूने के लिए के रूप में serialization मशीनरी मुफ्त में, और परिष्कृत serialization या प्रतिबिंब हमलों के चेहरे में भी कई तात्कालिकता के खिलाफ एक ironclad गारंटी प्रदान करता है। [...] एक एकल तत्व enum प्रकार एक सिंगलटन को लागू करने का सबसे अच्छा तरीका है।

2

1) कक्षा # 1 बहु-क्रम पर्यावरण

2) कक्षा # 2 आलसी आरंभीकरण और डबल जाँच की लॉकिंग के साथ एक सिंगलटन है के लिए अच्छा है, यह एक ज्ञात पैटर्न है और यह तुल्यकालन का उपयोग करने की जरूरत है। लेकिन आपका कार्यान्वयन टूट गया है, इसे क्षेत्र में volatile की आवश्यकता है। आप यह जान सकते हैं कि इस आलेख में क्यों http://www.javaworld.com/article/2074979/java-concurrency/double-checked-locking--clever--but-broken.html

3) एक विधि के साथ सिंगलटन को आलसी पैटर्न का उपयोग करने की आवश्यकता नहीं है, क्योंकि इसकी कक्षा लोड हो जाएगी और केवल पहले उपयोग में शुरू की जाएगी।

+0

आप कहने का मतलब है, मेरा पहला उदाहरण स्वयं दोनों प्रश्नों का उत्तर था। क्या मैं सही हूँ ? – Ravi

+0

हां, दूसरा सिर्फ आलसी init –

+3

हैवावर्ल्ड आलेख पुराना है और अब और लागू नहीं होता है। जेएमएम बदल दिया गया था ताकि डीसीएल अच्छी तरह से परिभाषित और काम करने की गारंटी दे। उस ने कहा, यह enum रास्ता के साथ जाना बेहतर है क्योंकि यह गलत पाने के लिए छोटा और कठिन है। – Kayaman

0
Double-checked_locking के अनुसार

, इसकी शायद सबसे अच्छा तरीका है

class Foo { 
    private volatile Helper helper; 
    public Helper getHelper() { 
     Helper result = helper; 
     if (result == null) { 
      synchronized(this) { 
       result = helper; 
       if (result == null) { 
        helper = result = new Helper(); 
       } 
      } 
     } 
     return result; 
    } 
} 

या Initialization-on-demand holder idiom

public class Something { 
    private Something() {} 

    private static class LazyHolder { 
     private static final Something INSTANCE = new Something(); 
    } 

    public static Something getInstance() { 
     return LazyHolder.INSTANCE; 
    } 
} 
संबंधित मुद्दे