2009-12-17 16 views
18

मैं ब्रायन गोएट्ज़ द्वारा "अभ्यास में जावा कंसुरेंसी इन प्रैक्टिस" से कोड नमूना देख रहा हूं। वह कहता है कि यह संभव है कि यह कोड एक अनंत लूप में रहेगा क्योंकि "तैयार 'का मान कभी भी पाठक धागे को दिखाई नहीं दे सकता है"। मुझे समझ नहीं आता कि यह कैसे हो सकता है ..."प्रैक्टिस में जावा कंसुरेंसी" उदाहरण के बारे में प्रश्न

public class NoVisibility { 
    private static boolean ready; 
    private static int number; 

    private static class ReaderThread extends Thread { 
     public void run() { 
      while (!ready) 
       Thread.yield(); 
      System.out.println(number); 
     } 
    } 

    public static void main(String[] args) { 
     new ReaderThread().start(); 
     number = 42; 
     ready = true; 
    } 
} 

उत्तर

27

क्योंकि readyvolatile के रूप में चिह्नित नहीं कर रहा है और क्योंकि यह while पाश के भीतर नहीं बदला गया है मूल्य while पाश के शुरू में कैश की जा सकती । यह जिटर कोड को अनुकूलित करने के तरीकों में से एक है।

तो यह संभव है कि धागा ready = true से पहले शुरू होता है और ready = false कैश पढ़ता है जो स्थानीय रूप से थ्रेड-स्थानीय रूप से पढ़ता है और इसे कभी नहीं पढ़ता है।

the volatile keyword देखें।

+0

यदि 'तैयार' अस्थिर है लेकिन संख्या नहीं है, तो क्या यह संभव है कि संख्या 0 के रूप में मुद्रित हो जाए? – zhiyuany

+0

हाय क्यूबिकिकस, यह सिर्फ एक स्पष्टीकरण है, यदि मुख्य धागा रीडर थ्रेड से पहले पूरा हो जाता है तो हमारे पास नंबर 42 सही करने का मौका है? –

8

कोड नमूना के साथ अनुभाग के बाद खंड में बताया गया है।

3.1.1 बासी डेटा

NoVisibility तरीके कि अपर्याप्त सिंक्रनाइज़ कार्यक्रमों आश्चर्य की बात परिणामों का कारण बन सकती पर प्रदर्शन किया: पुराना डेटा। जब पाठक धागा ready की जांच करता है, तो यह एक पुराना मूल्य देख सकता है। जब तक सिंक्रनाइज़ेशन का उपयोग नहीं किया जाता है प्रत्येक बार एक चर को तक पहुंचाया जाता है, तो उस चर के लिए एक पुराना मान देखना संभव है।

6

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

उदाहरण में, आपके द्वारा दी गई, JVM का अनुमान लगा सकते हैं कि ready क्षेत्र वर्तमान धागा भीतर संशोधित नहीं किया जा सकता है, तो यह false साथ !ready की जगह लेंगे, अनंत लूप के कारण। फ़ील्ड को volatile के रूप में चिह्नित करने से JVM प्रत्येक बार फ़ील्ड मान की जांच कर सकता है (या कम से कम सुनिश्चित करें कि ready चल रहे थ्रेड के प्रचार में परिवर्तन)।

+0

बस थोड़ा उत्सुक, क्यों 'झूठी' के साथ 'तैयार' को प्रतिस्थापित करने के लिए JVM इतना लापरवाह होगा? बाद में 'तैयार' 'रीडर थ्रेड 'के अंदर एक फ़ील्ड नहीं है। – sleepsort

+0

क्योंकि जब तक इसे अस्थिर के रूप में चिह्नित नहीं किया जाता है, तब तक जावा मेमोरी मॉडल के अनुसार JVM को इसे फिर से पढ़ने का कोई दायित्व नहीं है। यह अनुकूलन (परिवर्तनीय hoisting) वास्तव में काफी आम है। – assylias

2
private static boolean ready; 
private static int number; 

रास्ता स्मृति मॉडल काम कर सकते हैं कि प्रत्येक धागा पढ़ने जा सकता है और इन चरों की अपनी एक प्रतिलिपि के लिए लिख है (समस्या भी गैर स्थिर सदस्य चर को प्रभावित करता है)। अंतर्निहित वास्तुकला काम करने के तरीके का यह एक परिणाम है।

Jeremy Manson and Brian Goetz:

मल्टीप्रोसेसर प्रणालियों में, प्रोसेसर आम तौर पर कैश मेमोरी, जो दोनों (क्योंकि डेटा प्रोसेसर के करीब है) डेटा तक पहुँच तेजी और पर यातायात को कम करके प्रदर्शन में सुधार के एक या अधिक परतें होती हैं साझा मेमोरी बस (क्योंकि कई मेमोरी ऑपरेशंस स्थानीय कैश से संतुष्ट हो सकते हैं।) मेमोरी कैश प्रदर्शन में काफी सुधार कर सकते हैं, लेकिन वे कई नई चुनौतियों का सामना करते हैं। उदाहरण के लिए, क्या होता है जब दो प्रोसेसर एक ही समय में एक ही स्मृति स्थान की जांच करते हैं? वे किन स्थितियों के तहत एक ही मूल्य देखेंगे?

तो, अपने उदाहरण में, दो धागे अलग प्रोसेसर पर, अपने स्वयं के, अलग कैश में ready की एक प्रति के साथ चला सकता है प्रत्येक। जावा भाषा volatile और synchronized तंत्र प्रदान करता है ताकि यह सुनिश्चित किया जा सके कि धागे द्वारा देखे गए मान सिंक हो जाएं।

+0

सभी को धन्यवाद - अब समझ में आता है। तो यह इस तरह से विशिष्ट है कि JVMs जावा कोड को निष्पादन योग्य कोड में मैप करते हैं। मुझे लगता है कि सी ++ में समान चिंताएं उत्पन्न हो सकती हैं? क्या किसी को पता है कि सी ++ इस मुद्दे को कैसे संभालता है? –

4

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

अंतर्निहित हार्डवेयर के स्मृति मॉडल सी "उत्तराधिकारी" जैसी भाषाएं। C++ को एक औपचारिक मेमोरी मॉडल देने के लिए काम शुरू हो गया है ताकि सी ++ प्रोग्राम अलग-अलग प्लेटफॉर्म पर एक ही चीज़ का अर्थ हो सकें।

+0

मुझे नहीं पता कि यह असली ब्रायन गोएट्ज़ है, लेकिन वास्तव में इस बिंदु को घर चलाने के लिए, मुझे एक ऐसा उदाहरण देखना अच्छा लगेगा जो असफल हो। मैंने नमूना कोड की कोशिश की है और इसे कई निष्पादन के बाद भी असफल नहीं हो सकता है। कोई सलाह? – jbu

2
public class NoVisibility { 

    private static boolean ready = false; 
    private static int number; 

    private static class ReaderThread extends Thread { 

     @Override 
     public void run() { 
      while (!ready) { 
       Thread.yield(); 
      } 
      System.out.println(number); 
     } 
    } 

    public static void main(String[] args) throws InterruptedException { 
     new ReaderThread().start(); 
     number = 42; 
     Thread.sleep(20000); 
     ready = true; 
    } 
} 

प्लेस 20 सेकेंड क्या हो रहा है JIT उन 20 सेकेंड के दौरान में शुरू होगा और यह जांच का अनुकूलन और मूल्य को कैश या शर्त को पूरी तरह निकाल देंगे के लिए Thread.Sleep() कॉल। और इसलिए कोड दृश्यता पर असफल हो जाएगा।

ऐसा होने से रोकने के लिए आपको volatile का उपयोग करना होगा।

+0

यह परीक्षणों के दसियों के बाद विफल होने के लिए * प्रतीत नहीं होता है। जावा 1.8 का उपयोग कर – jbu

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