2010-03-09 12 views
11

मैं कैसे जल्दी से साबित कर सकता हूं कि निम्न वर्ग थ्रेड-सुरक्षित नहीं है (क्योंकि यह आलसी प्रारंभिकता का उपयोग करता है और सिंक्रनाइज़ेशन का उपयोग नहीं करता) कुछ कोड लिखकर? दूसरे शब्दों में, यदि मैं थ्रेड सुरक्षा के लिए निम्न श्रेणी का परीक्षण कर रहा हूं, तो मैं इसे कैसे विफल कर सकता हूं?निम्नलिखित कोड प्रदान करना धागा सुरक्षित नहीं है

public class LazyInitRace { 
    private ExpensiveObject instance = null; 

    public ExpensiveObject getInstance() { 
    if (instance == null) 
     instance = new ExpensiveObject(); 
    return instance; 
    } 
} 
+0

कन्स्ट्रक्टर सहायता में 'थ्रेड.sleep' जोड़ना होगा? – Amarghosh

+2

क्या आपके पास कोड का उपयोग करके इसे साबित करने के लिए * है, या आप इसे अन्य तरीकों से साबित कर सकते हैं? यह साबित करने के लिए एक आसान निष्पादन आरेख का उपयोग किया जा सकता है कि यह सुरक्षित नहीं है। – Herms

+0

साबित करने के लिए क्या है? यह थ्रेड-सुरक्षित नहीं है। – ChaosPandion

उत्तर

1

अच्छा ... इस कोड का परिणाम झूठा होगा, जहां आप एक सच्चाई की उम्मीद करेंगे।

import java.util.concurrent.Callable; 
import java.util.concurrent.ExecutionException; 
import java.util.concurrent.FutureTask; 

public class LazyInitRace { 

    public class ExpensiveObject { 
     public ExpensiveObject() { 
      try { 
       Thread.sleep(1000); 
      } catch (InterruptedException e) { 
      } 
     } 
    } 

    private ExpensiveObject instance = null; 

    public ExpensiveObject getInstance() { 
     if (instance == null) 
      instance = new ExpensiveObject(); 
     return instance; 
    } 

    public static void main(String[] args) { 
     final LazyInitRace lazyInitRace = new LazyInitRace(); 

     FutureTask<ExpensiveObject> target1 = new FutureTask<ExpensiveObject>(
       new Callable<ExpensiveObject>() { 

        @Override 
        public ExpensiveObject call() throws Exception { 
         return lazyInitRace.getInstance(); 
        } 
       }); 
     new Thread(target1).start(); 

     FutureTask<ExpensiveObject> target2 = new FutureTask<ExpensiveObject>(
       new Callable<ExpensiveObject>() { 

        @Override 
        public ExpensiveObject call() throws Exception { 
         return lazyInitRace.getInstance(); 
        } 
       }); 
     new Thread(target2).start(); 

     try { 
      System.out.println(target1.get() == target2.get()); 
     } catch (InterruptedException e) { 
     } catch (ExecutionException e) { 
     } 
    } 
} 
+0

अगर मैं कन्स्ट्रक्टर में Thread.sleep() को छोड़ देता हूं, तो यह सच हो जाता है। शायद अगर मैं इसे हजारों बार चलाता हूं, तो कभी-कभी मुझे झूठा लगेगा। – portoalet

12

आप एक लंबे समय के लिए अपने परीक्षण के भीतर निर्माण करने के लिए ले जाने की ExpensiveObject मजबूर कर सकते हैं? यदि ऐसा है, तो बस दो अलग-अलग धागे से दो बार getInstance() पर कॉल करें, कम समय में पहले कन्स्ट्रक्टर को दूसरे कॉल से पहले पूरा नहीं किया जाएगा। आप दो अलग-अलग उदाहरणों का निर्माण करेंगे, जहां आप असफल हो सकते हैं।

बेवकूफ डबल-चेक लॉकिंग असफल बनाना कठिन होगा, आपको दिमाग ... (भले ही यह चर के लिए volatile निर्दिष्ट किए बिना सुरक्षित नहीं है)।

+1

@ जोन स्कीट: कृपया मेरी अज्ञानता से क्षमा करें, लेकिन मैं अस्थिर के विशिष्ट अर्थ को जानना चाहूंगा? –

+1

अस्थिर एक जावा कीवर्ड है जो नियंत्रित करने के लिए प्रयोग किया जाता है कि जेवीएम थ्रेड के बीच मेमोरी एक्सेस कैसे संभालता है। –

-1

अच्छा, यह धागा सुरक्षित नहीं है। धागा सुरक्षा के प्रूफिंग यादृच्छिक बल्कि सरल है:

  1. ExpensiveObject निर्माता पूरी तरह से सुरक्षित बनाओ:

    सिंक्रनाइज़ ExpensiveObject() {...

  2. कोड है कि एक और प्रतिलिपि अगर जाँच करता है निर्माता के प्लेस वस्तु का अस्तित्व है - फिर अपवाद उठाएं।

  3. धागा सुरक्षित विधि 'उदाहरण' एक से अधिक थ्रेड से निष्पादन के लिए लूप को getInstance/clearInstance की चर

  4. प्लेस अनुक्रमिक कोड स्पष्ट और से (2)

+0

मुझे नहीं लगता कि वह * कोड थ्रेड को सुरक्षित बनाने के तरीकों के लिए पूछ रहा था, लेकिन * दिखाने के लिए * यह नहीं है कि यह नहीं है। –

+0

@ माइकल बोर्गवर्ड देखें (2) - अपवाद को उठाना असहायता दिखाता है। अन्य सभी सिंक्रनाइज़ किए गए चाल मैंने केवल पहले से मौजूद कोड में नई समस्या को पेश करने से बचने के लिए उपयोग किया है। – Dewfy

+0

आपका # 1 अमान्य है, यह संकलित नहीं होगा। "सिंक्रनाइज़" एक निर्माता के लिए एक वैध संशोधक नहीं है। सभी रचनाकार स्वाभाविक रूप से धागे सुरक्षित हैं। इसके अलावा, आपका प्रस्तावित समाधान अत्यधिक जटिल है ... बस उस स्थैतिक "getInstance" विधि को सिंक्रनाइज़ करें। "सबूत" – NateS

14

परिभाषा के अनुसार अपवाद प्रतीक्षा करने के लिए बनाएं , रेस स्थितियों का निर्धारण निश्चित रूप से नहीं किया जा सकता है, जब तक कि आप थ्रेड शेड्यूलर को नियंत्रित न करें (जो आप नहीं करते)। सबसे नज़दीक चीज जो आप कर सकते हैं या तो getInstance() विधि में कॉन्फ़िगर करने योग्य देरी जोड़ने के लिए है, या कोड समस्या प्रकट हो सकता है और इसे लूप में हजारों बार चला सकता है।

बीटीडब्ल्यू, इनमें से कोई भी वास्तव में "सबूत" का गठन नहीं करता है। Formal Verification, अपेक्षाकृत कम मात्रा में कोड के लिए भी करना बहुत मुश्किल है।

+5

+1 कोई भी प्रदर्शन जो यह विफल रहता है * साबित करता है कि यह असफल हो सकता है, इसलिए मुझे वास्तव में "सबूत" के उपयोग से कोई समस्या नहीं दिखाई दे रही है। – Seth

+3

शब्द के उचित उपयोग के लिए – Herms

+2

सहमत हुए। counterexample द्वारा सबूत एक वैध सबूत है। –

0

निर्माता में एक बहुत लंबा गणना रखो:

public ExpensiveObject() 
{ 
    for(double i = 0.0; i < Double.MAX_VALUE; ++i) 
    { 
     Math.pow(2.0,i); 
    } 
} 

आप अगर MAX_VALUE अपनी पसंद के लिए अधिक समय लगने पर एक बड़ी संख्या के द्वारा Double.MAX_VALUE/2.0 या विभाजन के समापन हालत में कमी कर सकते हैं।

3

चूंकि यह जावा है, तो आप अपने कोड में रुक जाता है या टूट जाता है इंजेक्षन और निष्पादन की एक से अधिक थ्रेड को नियंत्रित करने के thread-weaver लाइब्रेरी का उपयोग कर सकते हैं। इस तरह आप कन्स्ट्रक्टर कोड को संशोधित किए बिना ExpensiveObject कन्स्ट्रक्टर प्राप्त कर सकते हैं, जैसा कि अन्य ने (सही) सुझाव दिया है।

+0

पहले कभी उस पुस्तकालय के बारे में सुना नहीं है। काफी दिलचस्प लग रहा है। – Herms

5

यह कोड का उपयोग नहीं कर रहा है, लेकिन यहां एक उदाहरण है कि मैं इसे कैसे साबित करूंगा।मैं इस तरह निष्पादन आरेखों के लिए मानक प्रारूप भूल गया, लेकिन अर्थ पर्याप्त स्पष्ट होना चाहिए।

| Thread 1    | Thread 2    | 
|-----------------------|-----------------------| 
| **start**    |      | 
| getInstance()   |      | 
| if(instance == null) |      | 
| new ExpensiveObject() |      | 
| **context switch ->** | **start**    | 
|      | getInstance()   | 
|      | if(instance == null) | //instance hasn't been assigned, so this check doesn't do what you want 
|      | new ExpensiveObject() | 
| **start**    | **<- context switch** | 
| instance = result  |      | 
| **context switch ->** | **start**    | 
|      | instance = result  | 
|      | return instance  | 
| **start**    | **<- context switch** | 
| return instance  |      | 
0

आप इसे डीबगर के साथ आसानी से साबित कर सकते हैं।

  1. एक प्रोग्राम है जो getInstance() दो अलग-अलग धागे पर कॉल लिखें।
  2. ExpensiveObject के निर्माण पर ब्रेकपॉइंट सेट करें। सुनिश्चित करें कि डीबगर केवल थ्रेड को निलंबित कर देगा, वीएम नहीं।
  3. फिर जब पहला धागा ब्रेकपॉइंट पर बंद हो जाता है, तो इसे निलंबित कर दें।
  4. जब दूसरा धागा बंद हो जाता है, तो आप बस जारी रखते हैं।
  5. यदि आप getInstance() दोनों धागे, के लिए कॉल का परिणाम जांचते हैं तो वे अलग-अलग उदाहरणों का जिक्र करेंगे।

इस तरह से करने का लाभ यह है कि आपको वास्तव में एक महंगे ऑब्जेक्ट की आवश्यकता नहीं है, वास्तव में कोई भी वस्तु वास्तव में एक ही परिणाम उत्पन्न करेगी। आप बस कोड की उस विशेष पंक्ति के निष्पादन को निर्धारित करने के लिए डीबगर का उपयोग कर रहे हैं और इस प्रकार एक निर्धारिक परिणाम बना रहे हैं।

संबंधित मुद्दे