2012-03-08 9 views
14

मान लें कि हमारे पास बहुत ही सरल जावा क्लास MyClass है।
सिंक्रनाइज़ किए गए ब्लॉक के माध्यम से जावा में थ्रेड-सुरक्षित कक्षा

  1. यह सच अपरिवर्तनीय

    public class MyClass { 
        private final int number; 
    
        public MyClass(int number) { 
        this.number = number; 
        } 
    
        public int getNumber() { 
        return number; 
        } 
    
    } 
    
  2. मेक क्षेत्र numbervolatile करें:

    public class MyClass { 
        private int number; 
    
        public MyClass(int number) { 
         this.number = number; 
        } 
    
        public int getNumber() { 
         return number; 
        } 
    
        public void setNumber(int number) { 
         this.number = number; 
        } 
    } 
    

    तीन तरीके धागा सुरक्षित जावा वर्ग है जो किसी राज्य है निर्माण करने के लिए कर रहे हैं।

    public class MyClass { 
        private volatile int number; 
    
        public MyClass(int number) { 
        this.number = number; 
        } 
    
        public int getNumber() { 
         return number; 
        } 
    
        public void setNumber(int number) { 
         this.number = number; 
        } 
    } 
    
  3. एक synchronized ब्लॉक का उपयोग करें। अभ्यास में जावा कंसुरेंसी के अध्याय 4.3.5 में वर्णित इस दृष्टिकोण का क्लासिक संस्करण। और इस बारे में मजाकिया बात यह है कि इस पुस्तक के लिए इरेटा में उल्लिखित उदाहरण में एक त्रुटि है।

    public class MyClass { 
        private int number; 
    
        public MyClass(int number) { 
         setNumber(number); 
        } 
    
        public synchronized int getNumber() { 
         return number; 
        } 
    
        public synchronized void setNumber(int number) { 
         this.number = number; 
        } 
    } 
    

एक और तथ्य यह है कि चर्चा के संदर्भ में जोड़ा जाना चाहिए नहीं है। एक multhithreaded वातावरण में JVM synchronized ब्लॉक के बाहर निर्देशों को पुन: व्यवस्थित करने के लिए स्वतंत्र है जो तार्किक अनुक्रम को संरक्षित करता है और होता है- जेवीएम द्वारा निर्दिष्ट संबंधों से पहले होता है। यह प्रकाशन ऑब्जेक्ट का कारण बन सकता है जो अभी तक किसी अन्य थ्रेड के लिए ठीक से नहीं बनाया गया है।

मुझे तीसरे मामले के बारे में कुछ प्रश्न हैं। JVM intstructions को पुन: व्यवस्थित करने के लिए है और इसलिए डिफ़ॉल्ट मान के साथ वस्तु को प्रकाशित करने के लिए

public class MyClass { 
    private int number; 

    public MyClass(int number) { 
     synchronized (this){ 
      this.number = number; 
     } 
    } 

    public synchronized int getNumber() { 
     return number; 
    } 

    public synchronized void setNumber(int number) { 
     this.number = number; 
    } 
} 
  • एक पुनर्व्यवस्था तीसरे मामले में रोका जाएगा या यह संभव:

    1. यह कोड का एक टुकड़ा निम्नलिखित के बराबर होगी क्षेत्र में number?

    2. यदि दूसरे प्रश्न का उत्तर हाँ है तो मेरे पास एक और सवाल है।

      public class MyClass { 
          private int number; 
      
          public MyClass(int number) { 
           synchronized (new Object()){ 
            this.number = number; 
           } 
          } 
      
          public synchronized int getNumber() { 
           return number; 
          } 
      
          public synchronized void setNumber(int number) { 
           this.number = number; 
          } 
      } 
      

    इस अजीब दिखने synchronized (new Object()) पुन: क्रम प्रभाव को रोकने के लिए माना जाता है। क्या ये काम करेगा?

    बस स्पष्ट होने के लिए, इन सभी उदाहरणों में कोई व्यावहारिक अनुप्रयोग नहीं है। मैं multithreading की बारीकियों के बारे में सिर्फ उत्सुक हूँ।

  • उत्तर

    7

    synchronized(new Object()) कुछ भी नहीं करेगा, क्योंकि सिंक्रनाइज़ेशन केवल उस ऑब्जेक्ट पर है जिसे आप सिंक्रनाइज़ करते हैं। तो यदि थ्रेड ए oneObject पर सिंक्रनाइज़ करता है, और थ्रेड बी anotherObject पर सिंक्रनाइज़ करता है, तो उनके बीच कोई भी नहीं होता है। चूंकि हम इस तथ्य के बारे में जान सकते हैं कि कोई अन्य थ्रेड कभी भी new Object() पर सिंक्रनाइज़ नहीं करेगा, जो आप वहां बनाते हैं, यह किसी अन्य थ्रेड के बीच होने से पहले नहीं होगा।

    कन्स्ट्रक्टर में अपने synchronzied के संबंध में, यदि आपकी ऑब्जेक्ट सुरक्षित रूप से किसी अन्य थ्रेड पर प्रकाशित है, तो आपको इसकी आवश्यकता नहीं है; और यदि ऐसा नहीं है, तो आप शायद परेशानी की गड़बड़ी में हैं।मैंने इस प्रश्न को थोड़ी देर पहले समवर्ती-ब्याज सूची पर पूछा, और an interesting thread resulted। विशेष रूप से this email देखें, जो बताता है कि आपके कन्स्ट्रक्टर के साथ भी सुरक्षित प्रकाशन की अनुपस्थिति में, एक अन्य थ्रेड आपके फ़ील्ड में डिफ़ॉल्ट मान देख सकता है, और this email जो (imho) पूरी चीज़ को एक साथ जोड़ता है।

    +2

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

    +1

    @Affe एक थ्रेड के भीतर आप कोड को उन क्रम में देखने के लिए पहले से ही गारंटी दे रहे हैं - कोड सिंक्रनाइज़ेशन की आवश्यकता नहीं है। लेकिन डेटा रेस के कारण, एक ही थ्रेड को उसी क्रम में उन प्रभावों को देखने की आवश्यकता नहीं है। दूसरे शब्दों में, यदि थ्रेड ए 'ए, बी, सी' करता है (उदाहरण के लिए, 'ए' सिंक है,' बी' फ़ील्ड असाइनमेंट है, 'सी'' नया' के बाद संदर्भ सेट कर रहा है), थ्रेड बी अगर सीएबी' देखने की अनुमति है तो किनारे से पहले नहीं होता है - जो 'सिंक्रनाइज़ (नया ऑब्जेक्ट()) कभी प्रदान नहीं किया जाएगा। उदाहरण के लिए, यदि थ्रेड बी एक अलग कोर पर है, तो 'सी' 'ए' से पहले फ़्लश हो सकता है। – yshavit

    +2

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

    2

    प्रश्न में # 3, synchronized(new Object()) एक नो-ऑप है और कुछ भी नहीं रोकेगा। संकलक यह निर्धारित कर सकता है कि उस ऑब्जेक्ट पर संभवतः कोई अन्य थ्रेड सिंक्रनाइज़ नहीं हो सकता है (क्योंकि ऑब्जेक्ट तक कुछ भी नहीं पहुंच सकता है।) यह ब्रायन गोएट्ज के पेपर "Java theory and practice: Synchronization optimizations in Mustang" में एक स्पष्ट उदाहरण है।

    यहां तक ​​कि अगर आप एक निर्माता में सिंक्रनाइज़ करने की आवश्यकता थी, और भले ही आपके synchronized(new Object()) ब्लॉक उपयोगी था - यानी, आप एक अलग लंबे समय तक रहा वस्तु पर सिंक्रनाइज़ कर रहे थे, के बाद से अपने अन्य तरीकों this पर सिंक्रनाइज़ किए जाने के लिए, आपके पास दृश्यता समस्याएं यदि आप एक ही चर पर सिंक्रनाइज़ नहीं कर रहे हैं। यही कहना है, आप वास्तव में अपने कन्स्ट्रक्टर को synchronized(this) का उपयोग करना चाहते हैं।

    एक तरफ:this पर

    सिंक्रनाइज़ किया जा रहा खराब फार्म माना जाता है। इसके बजाय, कुछ निजी अंतिम फ़ील्ड पर सिंक्रनाइज़ करें। कॉलर आपकी ऑब्जेक्ट पर सिंक्रनाइज़ कर सकते हैं, जिससे डेडलॉक हो सकता है। निम्नलिखित पर विचार करें:

    public class Foo 
    { 
        private int value; 
        public synchronized int getValue() { return value; } 
        public synchronized void setValue(int value) { this.value = value; } 
    } 
    
    public class Bar 
    { 
        public static void deadlock() 
        { 
         final Foo foo = new Foo(); 
         synchronized(foo) 
         { 
          Thread t = new Thread() { public void run() { foo.setValue(1); } }; 
          t.start(); 
          t.join(); 
         } 
        } 
    } 
    

    यह Foo वर्ग है कि इस गतिरोध होगा की कॉल करने के लिए स्पष्ट नहीं है। अपने लॉकिंग सेमेन्टिक्स को अपनी कक्षा में आंतरिक और निजी रखने के लिए सर्वश्रेष्ठ।

    +0

    री पॉइंट 1, आप कन्स्ट्रक्टर से 'यह' लीक किए बिना किसी ऑब्जेक्ट को असुरक्षित रूप से प्रकाशित कर सकते हैं। उदाहरण के लिए, आप इसे एक गैर-'वोल्टाइल' स्थिर क्षेत्र में असाइन कर सकते हैं। उस स्थिति में, संदर्भ देखने के अपने अधिकार के भीतर एक और धागा अच्छी तरह से होगा, लेकिन इसके क्षेत्रों में लिखने के लिए नहीं। – yshavit

    +0

    @ यशवत: सहमत (हालांकि मैं उस मामले को "लीक कर रहा हूं" अगर मैं आपको सही तरीके से पढ़ रहा हूं)। गोएट्ज़ के पास एक और लेख था * जावा सिद्धांत और अभ्यास * सुरक्षित निर्माण तकनीकों के बारे में जो मैं एक लिंक जोड़ूंगा। –

    +2

    मैं @shavit - [जावा भाषा विशिष्टता 17.5] से सहमत हूं (http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.5) विशेष रूप से कहता है (और इसमें दिखाता है एक उदाहरण) कि गैर-अंतिम फ़ील्ड को सिंक्रनाइज़ेशन की आवश्यकता हो सकती है।अधिक विशेष रूप से: _ "एक ऑब्जेक्ट को पूरी तरह से प्रारंभ किया जाता है जब उसका कन्स्ट्रक्टर समाप्त हो जाता है। एक थ्रेड जो उस ऑब्जेक्ट के पूर्ण फ़ील्ड के लिए सही ढंग से प्रारंभ किए गए मानों को देखने के बाद किसी ऑब्जेक्ट का संदर्भ देख सकता है। "_। अंतिम शब्द पर ध्यान दें: गैर-अंतिम फ़ील्ड के लिए ऐसी कोई गारंटी मौजूद नहीं है। –

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