5

नीचे एक छोटी सी जावा कार्यक्रम है। इसमें "सीएनटी" नामक एक काउंटर है जिसे बढ़ाया जाता है और फिर "मॉनिटर" नामक एक सूची में जोड़ा जाता है। "cnt" को कई धागे से बढ़ाया जाता है, और मान कई थ्रेड द्वारा "मॉनिटर" में जोड़े जाते हैं।कन्करेंसी ब्लॉक सिंक्रनाइज़ का उपयोग कर अपेक्षित परिणाम देने नहीं

विधि के अंत में "जाने()", cnt और monitor.size() एक ही मूल्य है चाहिए, लेकिन वे नहीं है। monitor.size() में सही मान है।

आप टिप्पणी की सिंक्रनाइज़ ब्लॉकों में से एक uncommenting, और बाहर टिप्पणी वर्तमान में uncommented एक के बाद कोड को बदलते हैं, कोड अपेक्षित परिणाम पैदा करता है। साथ ही, यदि आप थ्रेड गिनती (THREAD_COUNT) को 1 पर सेट करते हैं, तो कोड अपेक्षित परिणाम उत्पन्न करता है।

यह केवल कई वास्तविक कोर के साथ एक मशीन पर पुनः प्रस्तुत किया जा सकता है।

public class ThreadTester { 

    private List<Integer> monitor = new ArrayList<Integer>(); 
    private Integer cnt = 0; 
    private static final int NUM_EVENTS = 2313; 
    private final int THREAD_COUNT = 13; 

    public ThreadTester() { 
    } 

    public void go() { 
     Runnable r = new Runnable() { 

      @Override 
      public void run() { 
       for (int ii=0; ii<NUM_EVENTS; ++ii) { 
        synchronized(monitor) { 
         synchronized(cnt) {  // <-- is this synchronized necessary? 
          monitor.add(cnt); 
         } 
//      synchronized(cnt) { 
//       cnt++;  // <-- why does moving the synchronized block to here result in the correct value for cnt? 
//      } 
        } 
        synchronized(cnt) { 
         cnt++;    // <-- why does moving the synchronized block here result in cnt being wrong? 
        } 
       } 
//    synchronized(cnt) { 
//     cnt += NUM_EVENTS; // <-- moving the synchronized block here results in the correct value for cnt, no surprise 
//    } 
      } 

     }; 
     Thread[] threads = new Thread[THREAD_COUNT]; 

     for (int ii=0; ii<THREAD_COUNT; ++ii) { 
      threads[ii] = new Thread(r); 
     } 
     for (int ii=0; ii<THREAD_COUNT; ++ii) { 
      threads[ii].start(); 
     } 
     for (int ii=0; ii<THREAD_COUNT; ++ii) { 
      try { threads[ii].join(); } catch (InterruptedException e) { } 
     } 

     System.out.println("Both values should be: " + NUM_EVENTS*THREAD_COUNT); 
     synchronized (monitor) { 
      System.out.println("monitor.size() " + monitor.size()); 
     } 
     synchronized (cnt) { 
      System.out.println("cnt " + cnt); 
     } 
    } 

    public static void main(String[] args) { 
     ThreadTester t = new ThreadTester(); 
     t.go(); 

     System.out.println("DONE"); 
    }  
} 

उत्तर

3

ठीक है की अलग अलग संभावनाएं पर एक नजर डालते हैं आप का उल्लेख:

1.

for (int ii=0; ii<NUM_EVENTS; ++ii) { 
    synchronized(monitor) { 
    synchronized(cnt) {  // <-- is this synchronized necessary? 
     monitor.add(cnt); 
    } 
    synchronized(cnt) { 
     cnt++;  // <-- why does moving the synchronized block to here result in the correct value for cnt? 
    } 
} 

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

2.

for (int ii=0; ii<NUM_EVENTS; ++ii) { 
    synchronized(monitor) { 
    monitor.add(cnt); 
    } 
    synchronized(cnt) { 
    cnt++;    // <-- why does moving the synchronized block here result in cnt being wrong? 
    } 
} 

ठीक है यह एक एक छोटा सा मुश्किल है। cnt एक पूर्णांक वस्तु है और जावा एक पूर्णांक वस्तु (पूर्णांकों अपरिवर्तनीय हैं) भले ही कोड से पता चलता है यह यहाँ क्या हो रहा है यह है कि संशोधित करने की अनुमति नहीं है। लेकिन वास्तव में क्या होगा यह है कि cnt ++ मूल्य cnt + 1 के साथ एक नया इंटीजर बनाएगा और सीएनटी ओवरराइड करेगा। यह वही कोड वास्तव में करता है:

synchronized(cnt) { 
    Integer tmp = new Integer(cnt + 1); 
    cnt = tmp; 
} 

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

यदि आप पहले सिंक्रनाइज़ किए गए ब्लॉक (मॉनीटर के साथ एक) को हटाते हैं, तो आपका परिणाम और भी गलत हो जाता है क्योंकि दौड़ की संभावना बढ़ जाती है।

सामान्य तौर पर आप ऐसा होने से रोकने के लिए एक ही अंतिम चर पर सिंक्रनाइज़ का उपयोग करने के लिए प्रयास करना चाहिए।

+0

आपका अवलोकन वास्तव में क्या हो रहा है यह है: इंटीजर tmp = नया इंटीजर (सीएनटी + 1); वह हिस्सा था जिसे मैं याद कर रहा था। मैं इस बात पर विचार करना भूल गया कि इसमें अपरिवर्तनीयता और ऑटोबॉक्सिंग कैसे खेलती है। – mangotang

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