जावा में सिंक्रनाइज़ (यह) से बचें?


381

जब भी जावा तुल्यकालन के बारे में SO पर कोई प्रश्न आता है, तो कुछ लोग यह बताने के लिए बहुत उत्सुक होते हैं कि synchronized(this)इससे बचना चाहिए। इसके बजाय, वे दावा करते हैं, एक निजी संदर्भ पर ताला पसंद किया जाना है।

दिए गए कारणों में से कुछ हैं:

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

मैं कुछ वास्तविक दुनिया के उदाहरणों को देखने में दिलचस्पी रखता हूं (कोई फोब्बर सामान नहीं है) जहां पर ताला लगाने से बचना thisबेहतर synchronized(this)होता है जब वह काम भी करेगा।

इसलिए: क्या आपको हमेशा synchronized(this)निजी संदर्भ पर ताला लगाने से बचना और बदलना चाहिए?


कुछ और जानकारी (उत्तर के रूप में अपडेट की गई हैं):

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

4
मैं यह भी बताना चाहता हूं कि यह संदर्भ महत्वपूर्ण है - "आमतौर पर यह उदाहरण पर ही लॉक होता है" बिट एक सशर्त रूप से थ्रेड-सुरक्षित वर्ग के दस्तावेजीकरण के बारे में है, जब आप लॉक को सार्वजनिक कर रहे होते हैं। दूसरे शब्दों में, यह वाक्य तब लागू होता है जब आप पहले ही यह निर्णय ले चुके होते हैं।
जॉन स्कीट

आंतरिक श्लेष की अनुपस्थिति में, और जब बाहरी शंख की आवश्यकता होती है, तो ताला अक्सर उदाहरण ही होता है, बलोच मूल रूप से कहता है। तो यह 'इस' पर लॉक के साथ आंतरिक सिंक के लिए मामला क्यों नहीं होगा? (प्रलेखन का महत्व एक और मुद्दा है।)
एलेजेन्सो

विस्तारित ग्रैन्युलैरिटी और अतिरिक्त सीपीयू कैश और बस अनुरोधों के बीच ओवरहेड के बीच एक व्यापार है, क्योंकि बाहरी वस्तु पर ताला लगाने से सीपीयू कैश (cf. MESIF और MOESI) के बीच एक अलग कैश लाइन को संशोधित करने और एक्सचेंज करने की आवश्यकता होगी।
आर्टेमग्र

1
मुझे लगता है, रक्षात्मक प्रोग्रामिंग की दुनिया में, आप मुहावरों से नहीं बल्कि कोड द्वारा बग को रोकते हैं। जब कोई मुझसे एक सवाल पूछता है, "आपका सिंक्रनाइज़ेशन कितना अनुकूलित है?", मैं "वेरी" के बजाय "वेरी" कहना चाहता हूं, जब तक कोई और मुहावरे का पालन नहीं करता है।
Sgene9

जवाबों:


132

मैं प्रत्येक बिंदु को अलग से कवर करूँगा।

  1. कुछ बुराई कोड आपके लॉक को चुरा सकते हैं (यह बहुत लोकप्रिय है, इसका "गलती से" संस्करण भी है)

    मैं गलती से चिंतित हूं । इसकी मात्रा यह है कि यह उपयोग thisआपकी कक्षा के 'उजागर इंटरफ़ेस' का हिस्सा है, और इसे प्रलेखित किया जाना चाहिए। कभी-कभी आपके लॉक का उपयोग करने के लिए अन्य कोड की क्षमता वांछित होती है। यह इस तरह की चीजों के बारे में सच है Collections.synchronizedMap(देखें जावदोक)।

  2. एक ही वर्ग के भीतर सभी सिंक्रनाइज़ किए गए तरीके सटीक उसी लॉक का उपयोग करते हैं, जो थ्रूपुट को कम करता है

    यह अत्यधिक सरलीकृत सोच है; सिर्फ synchronized(this)समस्या से छुटकारा नहीं मिलेगा। थ्रूपुट के लिए उचित तुल्यकालन अधिक विचार करेगा।

  3. आप (अनावश्यक रूप से) बहुत अधिक जानकारी उजागर कर रहे हैं

    यह # 1 का वेरिएंट है। उपयोग synchronized(this)आपके इंटरफ़ेस का हिस्सा है। यदि आप इसे उजागर नहीं करना चाहते / चाहती हैं, तो ऐसा न करें।


1. "सिंक्रोनाइज़" आपकी क्लास के 'एक्सपेक्टेड इंटरफेस' का हिस्सा नहीं है। 2. सहमत 3. देखें 1.
एलजेन्सो

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

15
समान। संग्रह के लिए Javadoc देखें। synchronizedMap () - लौटी हुई वस्तु आंतरिक रूप से सिंक्रनाइज़ (इस) का उपयोग करती है और वे उम्मीद करते हैं कि उपभोक्ता बड़े पैमाने पर परमाणु संचालन के लिए समान लॉक का उपयोग करने के लिए इसका उपयोग करें।
डैरन

3
वास्तव में Collections.synchronizedMap () आंतरिक रूप से सिंक्रनाइज़ (इस) का उपयोग नहीं करता है, यह एक निजी अंतिम लॉक ऑब्जेक्ट का उपयोग करता है।
बास लीजडेकर्स ने

1
@ बाइस लीजडेकर्स: प्रलेखन स्पष्ट रूप से निर्दिष्ट करता है कि सिंक्रनाइज़ेशन लौटे मानचित्र उदाहरण पर होता है। क्या दिलचस्प है, यह है कि विचार वापस आए keySet()और values()(उनके) पर लॉक न करें this, लेकिन नक्शा उदाहरण, जो सभी मानचित्र संचालन के लिए निरंतर व्यवहार प्राप्त करना महत्वपूर्ण है। कारण, लॉक ऑब्जेक्ट को एक चर में विभाजित किया जाता है, यह है कि SynchronizedSortedMapउप-मानचित्र को उप-मानचित्रों को लागू करने के लिए इसकी आवश्यकता होती है जो मूल मानचित्र उदाहरण पर लॉक होते हैं।
होल्गर

86

खैर, सबसे पहले यह बताया जाना चाहिए कि:

public void blah() {
  synchronized (this) {
    // do stuff
  }
}

शब्दार्थ के समतुल्य है:

public synchronized void blah() {
  // do stuff
}

जिसका उपयोग न करने का एक कारण है synchronized(this)। आप तर्क दे सकते हैं कि आप synchronized(this)ब्लॉक के आसपास सामान कर सकते हैं । सामान्य कारण यह है कि सिंक्रनाइज़ किए गए चेक को करने की कोशिश करने और उससे बचने के लिए, जो सभी प्रकार की संगामिति समस्याओं की ओर जाता है, विशेष रूप से दोहरी जाँच-लॉकिंग समस्या , जो यह दिखाने के लिए जाती है कि अपेक्षाकृत सरल जाँच करना कितना मुश्किल हो सकता है सुरक्षित धागा।

एक निजी लॉक एक रक्षात्मक तंत्र है, जो कभी भी एक बुरा विचार नहीं है।

इसके अलावा, जैसा कि आपने बताया, निजी ताले दानेदारता को नियंत्रित कर सकते हैं। किसी वस्तु पर परिचालन का एक सेट दूसरे से पूरी तरह से असंबंधित हो सकता है, लेकिन synchronized(this)उन सभी तक पहुंच को बाहर कर देगा।

synchronized(this) बस वास्तव में आपको कुछ भी नहीं देता है।


4
"सिंक्रनाइज़ (यह) वास्तव में आपको कुछ भी नहीं देता है।" ठीक है, मैं इसे एक सिंक्रोनाइज़ (myPStreetFinalLock) से बदल देता हूं। वह मुझे क्या देता है? आप इसे रक्षात्मक तंत्र होने की बात करते हैं। मैं किससे सुरक्षित हूं?
एलजेन्सो

14
आप बाहरी वस्तुओं द्वारा 'यह' के आकस्मिक (या दुर्भावनापूर्ण) लॉकिंग से सुरक्षित हैं।
क्लेटस

14
मैं इस जवाब से बिल्कुल भी सहमत नहीं हूँ: एक ताला हमेशा संभवतया कम से कम समय के लिए होना चाहिए, और यही कारण है कि आप पूरी विधि को सिंक्रनाइज़ करने के बजाय एक सिंक्रनाइज़ ब्लॉक के आसपास "सामान" करना चाहते हैं। ।
ओलिवियर

5
सिंक्रनाइज़ किए गए ब्लॉक के बाहर सामान करना हमेशा अच्छी तरह से इरादतन होता है। मुद्दा यह है कि लोगों को यह समय बहुत गलत लगता है और इसका एहसास भी नहीं होता है, ठीक उसी तरह जैसे डबल-चेक लॉकिंग समस्या में होता है। नरक का मार्ग अच्छे आशय से तैयार किया जाता है।
cletus

16
मैं सामान्य रूप से असहमत हूं "एक्स एक रक्षात्मक तंत्र है, जो कभी भी बुरा विचार नहीं है।" इस रवैये की वजह से वहां अनावश्यक रूप से ब्लॉटेड कोड की भरमार है।
12

54

जब आप सिंक्रनाइज़ (यह) का उपयोग कर रहे हैं तो आप वर्ग उदाहरण का उपयोग लॉक के रूप में कर रहे हैं। इसका मतलब यह है कि जबकि लॉक थ्रेड 1 द्वारा अधिग्रहित है , थ्रेड 2 को इंतजार करना चाहिए।

मान लीजिए कि निम्नलिखित कोड है:

public void method1() {
    // do something ...
    synchronized(this) {
        a ++;      
    }
    // ................
}


public void method2() {
    // do something ...
    synchronized(this) {
        b ++;      
    }
    // ................
}

विधि 1 चर को संशोधित करने के लिए एक और विधि 2 संशोधित चर , दो धागे से एक ही चर के समवर्ती संशोधन से बचा जाना चाहिए और यह है। लेकिन जब thread1 को संशोधित करने के लिए एक और thread2 बदलाव इसे किसी भी दौड़ की स्थिति के बिना किया जा सकता है।

दुर्भाग्य से, उपरोक्त कोड इसे अनुमति नहीं देगा क्योंकि हम एक लॉक के लिए एक ही संदर्भ का उपयोग कर रहे हैं; इसका मतलब यह है कि थ्रेड्स भले ही वे रेस की स्थिति में न हों, प्रतीक्षा करनी चाहिए और जाहिर है कि कोड प्रोग्राम की संगति का बलिदान करता है।

समाधान दो अलग-अलग चर के लिए 2 अलग-अलग तालों का उपयोग करना है :

public class Test {

    private Object lockA = new Object();
    private Object lockB = new Object();

    public void method1() {
        // do something ...
        synchronized(lockA) {
            a ++;      
        }
        // ................
    }


    public void method2() {
        // do something ...
        synchronized(lockB) {
            b ++;      
        }
        // ................
    }

}

ऊपर दिए गए उदाहरण में अधिक बारीक दाने वाले ताले का उपयोग किया गया है (एक के बजाय 2 ताले और क्रमशः और बी के लिए लॉकए और लॉकबी ), और परिणामस्वरूप बेहतर संगामिति की अनुमति देता है, दूसरी ओर यह पहले उदाहरण की तुलना में अधिक जटिल हो गया ...


यह बहुत खतरनाक है। अब आपने एक क्लाइंट-साइड (इस वर्ग का उपयोगकर्ता) लॉक ऑर्डरिंग आवश्यकता को पेश किया है। यदि दो थ्रेड्स एक अलग क्रम में method1 () और method2 () कॉल कर रहे हैं, तो वे गतिरोध की संभावना रखते हैं, लेकिन इस वर्ग के उपयोगकर्ता को यह पता नहीं है कि यह मामला है।
daveb

7
"सिंक्रनाइज़ (यह)" द्वारा प्रदान नहीं की गई ग्रैन्युलैरिटी मेरे प्रश्न के दायरे से बाहर है। और अपने ताला क्षेत्रों अंतिम नहीं होना चाहिए?
इलाजेन्सो

10
एक गतिरोध के क्रम में हमें ए से ब्लॉक द्वारा ब्लॉक किए गए ब्लॉक बी। डीएवीबी द्वारा सिंक्रनाइज़ किए गए कॉल को करना चाहिए, आप गलत हैं ...
एंड्रियास बाकुरोव

3
इस उदाहरण में कोई गतिरोध नहीं है जहाँ तक मैं देख सकता हूँ। मैं स्वीकार करता हूं कि यह सिर्फ छद्म धर्म है, लेकिन मैं java.util.concurrent.locks के कार्यान्वयन में से एक का उपयोग करूंगा। java.util.concurrent.locks.ReentrantLock
Shawer Vader

15

जबकि मैं हठधर्मिता नियमों का आँख बंद करके पालन नहीं करने के बारे में सहमत हूं, क्या "लॉक चोरी" परिदृश्य आपको इतना विलक्षण लगता है? एक धागा वास्तव में आपकी वस्तु "बाहरी" पर लॉक प्राप्त कर सकता है ( synchronized(theObject) {...}), सिंक्रनाइज़ किए गए इंस्टेंस विधियों पर प्रतीक्षा कर रहे अन्य थ्रेड्स को अवरुद्ध करना।

यदि आप दुर्भावनापूर्ण कोड में विश्वास नहीं करते हैं, तो विचार करें कि यह कोड तीसरे पक्ष से आ सकता है (उदाहरण के लिए यदि आप किसी प्रकार का एप्लिकेशन सर्वर विकसित करते हैं)।

"आकस्मिक" संस्करण की संभावना कम लगती है, लेकिन जैसा कि वे कहते हैं, "कुछ इडियट-प्रूफ बनाएं और कोई बेहतर बेवकूफ का आविष्कार करेगा"।

इसलिए मैं इसके साथ सहमत हूं कि यह निर्भर करता है कि क्लास-ऑफ-द-क्लास क्या सोचता है।


निम्नलिखित eljenso की पहली 3 टिप्पणियों को संपादित करें:

मैंने कभी भी चोरी करने की समस्या का अनुभव नहीं किया है लेकिन यहाँ एक काल्पनिक परिदृश्य है:

मान लीजिए कि आपका सिस्टम एक सर्वलेट कंटेनर है, और जिस वस्तु पर हम विचार कर रहे हैं, वह ServletContextकार्यान्वयन है। इसकी getAttributeविधि थ्रेड-सुरक्षित होनी चाहिए, क्योंकि संदर्भ विशेषताएँ साझा डेटा हैं; इसलिए आप इसे घोषित करते हैं synchronized। आइए यह भी कल्पना करें कि आप अपने कंटेनर कार्यान्वयन के आधार पर एक सार्वजनिक होस्टिंग सेवा प्रदान करते हैं।

मैं आपका ग्राहक हूं और आपकी साइट पर मेरा "अच्छा" सर्वलेट तैनात करता हूं। ऐसा होता है कि मेरे कोड में कॉल है getAttribute

एक हैकर, जो दूसरे ग्राहक के रूप में प्रच्छन्न है, आपकी दुर्भावनापूर्ण सर्वलेट को आपकी साइट पर दिखाता है। इसमें निम्नलिखित कोड हैinit विधि :

सिंक्रनाइज़ (यह .getSletletConfig ()। getServletContext ()) {
   जबकि (सच) {}
}

यह मानते हुए कि हम एक ही सर्वलेट प्रसंग साझा करते हैं (जब तक कि दो सर्वलेट एक ही वर्चुअल होस्ट पर नहीं हैं तब तक युक्ति द्वारा अनुमति दी जाती है), मेरी कॉल getAttributeहमेशा के लिए लॉक हो जाती है। हैकर ने मेरे सर्वलेट पर एक DoS हासिल किया है।

यह हमला संभव नहीं है अगर getAttributeनिजी लॉक पर सिंक्रनाइज़ होने पर , क्योंकि 3-पार्टी कोड इस लॉक को अधिग्रहित नहीं कर सकता है।

मैं स्वीकार करता हूं कि उदाहरण के साथ विवाद होता है और सर्वलेट कंटेनर कैसे काम करता है, इसका एक ओवरसिम्प्लेस्टिक दृष्टिकोण है, लेकिन IMHO यह बात साबित करता है।

इसलिए मैं सुरक्षा के विचार के आधार पर अपनी डिजाइन पसंद बनाऊंगा: क्या मेरा उस कोड पर पूरा नियंत्रण होगा जिसका उपयोग इंस्टेंस पर है? अनिश्चित काल तक किसी थ्रेड के लॉक को धारण करने का परिणाम क्या होगा?


यह निर्भर करता है कि किस-किस वर्ग का है: यदि यह एक 'महत्वपूर्ण' वस्तु है, तो निजी संदर्भ पर लॉक करें? पर्याप्त उदाहरण लॉकिंग पर्याप्त होगा?
इलाजेन्सो

6
हां, लॉक चोरी का परिदृश्य मुझे बहुत दूर की बात लगता है। हर कोई इसका उल्लेख करता है, लेकिन वास्तव में किसने किया है या इसका अनुभव किया है? यदि आप "गलती से" एक ऑब्जेक्ट को लॉक करते हैं, जिसे आपको नहीं करना चाहिए, तो इस प्रकार की स्थिति के लिए एक नाम है: यह एक बग है। इसे ठीक करो।
एलजेन्सो

4
इसके अलावा, आंतरिक संदर्भों पर लॉकिंग "बाहरी सिंक्रोन अटैक" से मुक्त नहीं है: यदि आप जानते हैं कि कोड का एक निश्चित सिंक किए गए भाग के बाहरी घटना होने का इंतजार करता है (जैसे फ़ाइल लिखना, डीबी में मान, टाइमर घटना) तो आप शायद कर सकते हैं साथ ही इसे ब्लॉक करने की व्यवस्था करें।
एलजेन्सो

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

12

यह स्थिति पर निर्भर करता है।
यदि केवल एक साझा इकाई है या एक से अधिक है।

पूर्ण कार्य उदाहरण यहाँ देखें

एक छोटा सा परिचय।

थ्रेड्स और साझा करने योग्य
इकाइयाँ कई थ्रेड्स के लिए एक ही इकाई तक पहुँचना संभव है, उदाहरण के लिए, कई कनेक्शनट्रेड्स एक संदेश संदेश साझा करते हैं। चूंकि सूत्र समवर्ती रूप से चलते हैं, इसलिए किसी के डेटा को एक दूसरे द्वारा ओवरराइड करने का मौका हो सकता है जो एक गड़बड़ स्थिति हो सकती है।
इसलिए हमें यह सुनिश्चित करने के लिए किसी तरह की आवश्यकता है कि एक समय में केवल एक थ्रेड द्वारा ही साझा करने योग्य इकाई तक पहुँचा जा सके। (संगामिति)।

सिंक्रोनाइज्ड ब्लॉक
सिंक्रोनाइज्ड () ब्लॉक एक प्रकार का छोटा पौधा है।
सबसे पहले, एक छोटे सादृश्य
मान लीजिए कि दो-व्यक्ति P1, P2 (थ्रेड्स) एक वॉशरूम के अंदर एक वॉशबेसिन (शालीन इकाई) हैं और एक दरवाजा (लॉक) है।
अब हम चाहते हैं कि एक समय में एक व्यक्ति वॉशबेसिन का उपयोग करे।
P1 द्वारा दरवाजे को लॉक करने के लिए एक दृष्टिकोण है जब दरवाजा लॉक किया जाता है P2 प्रतीक्षा करता है जब तक कि p1 अपना काम पूरा नहीं करता है
P1 दरवाजे को अनलॉक करता है
तब केवल p1 वॉशबेसिन का उपयोग कर सकता है।

वाक्य - विन्यास।

synchronized(this)
{
  SHARED_ENTITY.....
}

"इस" ने क्लास के साथ जुड़े आंतरिक लॉक प्रदान किए (जावा डेवलपर ने ऑब्जेक्ट क्लास को इस तरह से डिज़ाइन किया कि प्रत्येक ऑब्जेक्ट मॉनिटर के रूप में काम कर सके)। उपरोक्त दृष्टिकोण ठीक काम करता है जब केवल एक साझा इकाई और कई धागे होते हैं (1: एन)। एन शरेबल इकाइयां-एम थ्रेड्स अब एक स्थिति के बारे में सोचते हैं जब एक वॉशरूम के अंदर दो वॉशबेसिन और केवल एक दरवाजा होता है। यदि हम पिछले दृष्टिकोण का उपयोग कर रहे हैं, तो केवल पी 1 एक वॉशबेसिन का उपयोग कर सकता है जबकि पी 2 बाहर इंतजार करेगा। यह संसाधन का अपव्यय है क्योंकि कोई भी बी 2 (वॉशबेसिन) का उपयोग नहीं कर रहा है। एक समझदार दृष्टिकोण वॉशरूम के अंदर एक छोटा कमरा बनाना और उन्हें वॉशबेसिन के लिए एक दरवाजा प्रदान करना होगा। इस तरह, पी 1 बी 1 तक पहुंच सकता है और पी 2 बी 2 और इसके विपरीत तक पहुंच सकता है।
यहां छवि विवरण दर्ज करें

washbasin1;  
washbasin2;

Object lock1=new Object();
Object lock2=new Object();

  synchronized(lock1)
  {
    washbasin1;
  }

  synchronized(lock2)
  {
    washbasin2;
  }

यहां छवि विवरण दर्ज करें
यहां छवि विवरण दर्ज करें

थ्रेड्स पर और देखें ----> यहाँ


11

इस पर C # और Java शिविरों में एक अलग आम सहमति प्रतीत होती है। जावा कोड के अधिकांश उपयोग मैंने देखे हैं:

// apply mutex to this instance
synchronized(this) {
    // do work here
}

जबकि C # कोड का अधिकांश हिस्सा यकीनन सुरक्षित है:

// instance level lock object
private readonly object _syncObj = new object();

...

// apply mutex to private instance level field (a System.Object usually)
lock(_syncObj)
{
    // do work here
}

C # मुहावरा निश्चित रूप से सुरक्षित है। जैसा कि पहले उल्लेख किया गया है, उदाहरण के बाहर से लॉक के लिए कोई दुर्भावनापूर्ण / आकस्मिक उपयोग नहीं किया जा सकता है। जावा कोड में यह जोखिम भी है, लेकिन ऐसा लगता है कि जावा समुदाय ने समय के साथ थोड़ा कम सुरक्षित, लेकिन थोड़ा अधिक संस्करण के रूप में गुरुत्वाकर्षण किया है।

यह जावा के खिलाफ एक खुदाई के रूप में नहीं है, बस दोनों भाषाओं पर काम करने के मेरे अनुभव का प्रतिबिंब है।


3
शायद चूंकि C # एक छोटी भाषा है, इसलिए उन्होंने बुरे पैटर्न से सीखा जो जावा कैंप और इस तरह के कोड सामान में बेहतर थे? क्या कम सिंगलटन भी हैं? :)
बिल K

3
उसने कहा। काफी सच है, लेकिन मैं चारा के लिए नहीं जा रहा हूँ! मुझे लगता है कि मैं निश्चित रूप से कह सकता हूं कि C # कोड में और भी बड़े अक्षर हैं;)
serg10

1
बस सच नहीं है (इसे अच्छी तरह से लगाने के लिए)
tcurdt

7

java.util.concurrentपैकेज बेहद मेरी धागा सुरक्षित कोड की जटिलता कम हो गया है। मेरे पास केवल जाने के लिए महत्वपूर्ण सबूत हैं, लेकिन सबसे ज्यादा काम मैंने देखा हैsynchronized(x) , वह एक लॉक, सेमाफोर या लाच को फिर से लागू करने के लिए प्रतीत होता है, लेकिन निचले स्तर के मॉनिटर का उपयोग करते हुए।

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


6
  1. यदि संभव हो तो अपना डेटा अपरिवर्तनीय बनाएं ( final चर)
  2. यदि आप कई थ्रेड्स में साझा किए गए डेटा के म्यूटेशन से बच नहीं सकते हैं, तो उच्च स्तरीय प्रोग्रामिंग कंस्ट्रक्शंस का उपयोग करें [जैसे बारीक Lockएपीआई]

एक लॉक साझा संसाधन के लिए अनन्य पहुँच प्रदान करता है: एक समय में केवल एक धागा लॉक का अधिग्रहण कर सकता है और साझा संसाधन तक सभी पहुंच के लिए आवश्यक है कि लॉक को पहले अधिग्रहित किया जाए।

नमूना कोड का उपयोग करने के लिए ReentrantLockजो Lockइंटरफ़ेस लागू करता है

 class X {
   private final ReentrantLock lock = new ReentrantLock();
   // ...

   public void m() {
     lock.lock();  // block until condition holds
     try {
       // ... method body
     } finally {
       lock.unlock()
     }
   }
 }

सिंक्रोनाइज्ड पर लॉक का लाभ (यह)

  1. सिंक्रनाइज़ किए गए तरीकों या बयानों का उपयोग सभी लॉक अधिग्रहण और रिलीज को ब्लॉक-स्ट्रक्चर्ड तरीके से होने के लिए मजबूर करता है।

  2. लॉक कार्यान्वयन प्रदान करके सिंक्रनाइज़ किए गए तरीकों और कथनों के उपयोग पर अतिरिक्त कार्यक्षमता प्रदान करते हैं

    1. लॉक प्राप्त करने का गैर-अवरोधक प्रयास ( tryLock())
    2. बाधित होने वाले लॉक को प्राप्त करने का प्रयास ( lockInterruptibly())
    3. लॉकआउट को प्राप्त करने का प्रयास जो टाइमआउट ( tryLock(long, TimeUnit)) हो सकता है।
  3. एक लॉक क्लास भी व्यवहार और शब्दार्थ प्रदान कर सकता है जो कि अंतर्निहित मॉनिटर लॉक से काफी अलग है, जैसे कि

    1. गारंटी देने का आदेश
    2. गैर-री एंट्रेंट उपयोग
    3. गतिरोध का पता लगाने

विभिन्न प्रकार के संबंध में इस एसई प्रश्न पर एक नजर डालें Locks :

सिंक्रनाइज़ेशन बनाम लॉक

आप सिंक्रोनाइज़्ड ब्लॉक्स के बजाय एडवांस्ड कंसिस्टेंसी एपीआई का इस्तेमाल करके थ्रेड सेफ्टी हासिल कर सकते हैं। यह प्रलेखन पृष्ठ थ्रेड सुरक्षा प्राप्त करने के लिए अच्छे प्रोग्रामिंग कंस्ट्रक्शन प्रदान करता है।

लॉक ऑब्जेक्ट्स लॉकिंग मुहावरों का समर्थन करते हैं जो कई समवर्ती अनुप्रयोगों को सरल बनाते हैं।

निष्पादकों शुरू करने और धागे के प्रबंधन के लिए एक उच्च स्तरीय एपीआई परिभाषित करते हैं। Java.util.concurrent द्वारा प्रदान किए गए निष्पादन कार्यान्वयन बड़े पैमाने के अनुप्रयोगों के लिए उपयुक्त थ्रेड पूल प्रबंधन प्रदान करते हैं।

समवर्ती संग्रह डेटा के बड़े संग्रह को प्रबंधित करना आसान बनाते हैं, और सिंक्रनाइज़ेशन की आवश्यकता को बहुत कम कर सकते हैं।

परमाणु चर में ऐसी विशेषताएं होती हैं जो सिंक्रनाइज़ेशन को कम करती हैं और मेमोरी संगतता त्रुटियों से बचने में मदद करती हैं।

थ्रेडलोकल्रैंडम (JDK 7 में) कई थ्रेड्स से कुशल जेनरेशन की छद्म संख्या प्रदान करता है।

अन्य प्रोग्रामिंग कंस्ट्रक्शन के लिए java.util.concurrent और java.util.concurrent.atomic पैकेज देखें ।


5

यदि आपने तय किया है कि:

  • आपको जिस चीज़ की ज़रूरत है, वह वर्तमान ऑब्जेक्ट पर लॉक है; तथा
  • आप इसे पूरी विधि से छोटे ग्रैन्युलैरिटी के साथ लॉक करना चाहते हैं;

तब मुझे सिंक्रोनाइज़ (इस) पर एक टैबू नहीं दिखता है।

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

इस सवाल के रूप में कि आपको किस ऑब्जेक्ट के लॉक का उपयोग करना चाहिए, मुझे लगता है कि वर्तमान ऑब्जेक्ट पर सिंक्रोनाइज़ करने में कुछ भी गलत नहीं है यदि आप जो कर रहे हैं उसके तर्क से यह उम्मीद की जाएगी और आपकी कक्षा आमतौर पर कैसे उपयोग की जाएगी । उदाहरण के लिए, एक संग्रह के साथ, वह वस्तु जिसे आप तार्किक रूप से लॉक करने की उम्मीद करेंगे, वह आमतौर पर संग्रह ही है।


1
"अगर यह तर्क से उम्मीद की जाएगी ..." एक बिंदु है जिसे मैं भी प्राप्त करने की कोशिश कर रहा हूं। मैं हमेशा निजी ताले का उपयोग करने की बात नहीं देखता , हालांकि आम सहमति से ऐसा लगता है कि यह बेहतर है, क्योंकि यह चोट नहीं करता है और अधिक रक्षात्मक है।
इलाजेन्सो

4

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

संक्षेप में

  1. विधि के स्तर पर सिंक्रोनाइज़ करना क्लाइंट साइड लॉकिंग की अनुमति देता है।
  2. यदि आपके पास एक निजी लॉक ऑब्जेक्ट है - यह क्लाइंट साइड लॉकिंग को असंभव बनाता है। यह ठीक है यदि आप जानते हैं कि आपकी कक्षा में "अनुपस्थित है तो" प्रकार की कार्यक्षमता नहीं है।
  3. यदि आप एक पुस्तकालय डिजाइन कर रहे हैं - तो इस पर सिंक्रनाइज़ करना या विधि को सिंक्रनाइज़ करना अक्सर समझदार होता है। क्योंकि आप शायद ही यह तय करने की स्थिति में हों कि आपकी कक्षा का उपयोग कैसे होने वाला है।
  4. अगर वेक्टर एक निजी लॉक ऑब्जेक्ट का उपयोग करता था - तो "अनुपस्थित" सही होने पर इसे प्राप्त करना असंभव होगा। क्लाइंट कोड कभी भी निजी लॉक को हैंडल नहीं करेगा, इसलिए अपने राज्य की सुरक्षा के लिए EXACT SAME LOCK का उपयोग करने के मौलिक नियम को तोड़ देगा।
  5. इस पर या सिंक्रनाइज़ किए गए तरीकों को सिंक्रनाइज़ करने में समस्या होती है क्योंकि अन्य लोगों ने बताया है - किसी को ताला मिल सकता है और इसे कभी भी जारी नहीं किया जा सकता है। अन्य सभी धागे ताला बंद होने का इंतजार करते रहेंगे।
  6. इसलिए जानें कि आप क्या कर रहे हैं और जो सही है उसे अपनाएं।
  7. किसी ने तर्क दिया कि एक निजी लॉक ऑब्जेक्ट होने से आपको बेहतर ग्रैन्युलैरिटी मिलती है - जैसे कि यदि दो ऑपरेशन असंबंधित हैं - तो उन्हें अलग-अलग तालों द्वारा संरक्षित किया जा सकता है जिसके परिणामस्वरूप बेहतर थ्रूपुट हैं। लेकिन मुझे लगता है कि डिजाइन गंध है और कोड गंध नहीं है - अगर दो संचालन पूरी तरह से असंबंधित हैं तो वे एसएएमई वर्ग का हिस्सा क्यों हैं? एक क्लास क्लब को असंबंधित कार्य क्यों करना चाहिए? एक उपयोगिता वर्ग हो सकता है? Hmmmm - एक ही उदाहरण के माध्यम से स्ट्रिंग हेरफेर और कैलेंडर दिनांक स्वरूपण प्रदान करने के लिए कुछ उपयोग ?? ... कम से कम मेरे लिए कोई मतलब नहीं है !!

3

नहीं, आपको हमेशा नहीं करना चाहिए । हालाँकि, मैं इससे बचता हूँ जब किसी विशेष वस्तु पर कई चिंताएँ होती हैं जो केवल स्वयं के संबंध में थ्रेडसेफ़ होनी चाहिए। उदाहरण के लिए, आपके पास एक परिवर्तनशील डेटा ऑब्जेक्ट हो सकता है जिसमें "लेबल" और "पैरेंट" फ़ील्ड हैं; इनमें से थ्रेडसेफ़ की जरूरत है, लेकिन एक को बदलने की जरूरत नहीं है कि दूसरे को लिखा / पढ़ा जा रहा है। (व्यवहार में मैं इसे क्षेत्रों को अस्थिर घोषित करने और / या java.util.concurrent के एटॉमिकफू रैपर का उपयोग करके) से बचूंगा।

सामान्य रूप से सिंक्रनाइज़ेशन थोड़ा अनाड़ी है, क्योंकि यह सोचने के बजाय एक बड़े लॉक को थप्पड़ मारता है कि कैसे धागे को एक दूसरे के आसपास काम करने की अनुमति दी जा सकती है। उपयोग करना synchronized(this)यहां तक ​​कि अनाड़ी और असामाजिक है, क्योंकि यह कह रहा है "कोई भी कुछ भी बदल सकता है इस वर्ग पर जबकि मैं लॉक रखता हूं"। वास्तव में आपको कितनी बार ऐसा करने की आवश्यकता है?

मैं अधिक बल्कि अधिक दानेदार ताले होगा; यहां तक ​​कि अगर आप सब कुछ बदलने से रोकना चाहते हैं (शायद आप वस्तु को क्रमबद्ध कर रहे हैं), तो आप एक ही चीज़ को प्राप्त करने के लिए सभी तालों को प्राप्त कर सकते हैं, साथ ही यह उस तरह से अधिक स्पष्ट है। जब आप उपयोग करते हैं synchronized(this), तो यह बिल्कुल स्पष्ट नहीं है कि आप सिंक्रनाइज़ क्यों कर रहे हैं, या दुष्प्रभाव क्या हो सकते हैं। यदि आप उपयोग करते हैं synchronized(labelMonitor), या इससे भी बेहतर labelLock.getWriteLock().lock(), यह स्पष्ट है कि आप क्या कर रहे हैं और आपके महत्वपूर्ण अनुभाग के प्रभाव क्या हैं।


3

संक्षिप्त उत्तर : आपको अंतर को समझना होगा और कोड के आधार पर चुनाव करना होगा।

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


क्या मैं आपको सही ढंग से समझता हूं जब आप कहते हैं कि यह आपके अनुभव पर निर्भर करता है?
इलाजेन्सो

पहली जगह में यह उस कोड पर निर्भर करता है जिसे आप लिखना चाहते हैं। केवल यह कहते हुए कि आपको सिंक्रोनाइज़ (इस) का उपयोग न करने के लिए डायवर्ट करने के लिए थोड़ा और अनुभव की आवश्यकता हो सकती है।
tcurdt

2

एक लॉक का उपयोग या तो दृश्यता के लिए या समवर्ती संशोधन से कुछ डेटा की रक्षा के लिए किया जाता है जो दौड़ को जन्म दे सकता है।

जब आपको परमाणु बनने के लिए सिर्फ आदिम प्रकार के संचालन करने की आवश्यकता होती है, तो उपलब्ध विकल्प AtomicIntegerऔर पसंद हैं।

लेकिन मान लें कि आपके पास दो पूर्णांक हैं जो एक दूसरे से संबंधित हैं जैसे कि xऔर yनिर्देशांक, जो एक दूसरे से संबंधित हैं और इसे परमाणु तरीके से बदलना चाहिए। फिर आप एक ही लॉक का उपयोग करके उनकी रक्षा करेंगे।

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

class Point{
   private int x;
   private int y;

   public Point(int x, int y){
       this.x = x;
       this.y = y;
   }

   //mutating methods should be guarded by same lock
   public synchronized void changeCoordinates(int x, int y){
       this.x = x;
       this.y = y;
   }
}

उपर्युक्त उदाहरण में मेरे पास केवल एक ही विधि है जो दोनों को परिवर्तित करती है xऔर yन कि दो अलग-अलग विधियों के रूप में xऔर yसंबंधित हैं और यदि मैंने उत्परिवर्तन xऔर yअलग-अलग के लिए दो अलग-अलग तरीके दिए हैं तो यह थ्रेड सुरक्षित नहीं होगा।

यह उदाहरण केवल प्रदर्शित करने के लिए है और जरूरी नहीं कि जिस तरह से इसे लागू किया जाना चाहिए। इसे करने का सबसे अच्छा तरीका यह होगा कि इसे IMMUTABLE बनाया जाए

अब Pointउदाहरण के विरोध में , TwoCounters@Andreas द्वारा पहले से ही एक उदाहरण प्रदान किया गया है जहां राज्य दो अलग-अलग तालों द्वारा संरक्षित किया जा रहा है क्योंकि राज्य एक-दूसरे से असंबंधित हैं।

असंबंधित राज्यों की रक्षा के लिए विभिन्न तालों के उपयोग की प्रक्रिया को लॉक स्ट्रिपिंग या लॉक विभाजन कहा जाता है


1

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

पाठक के दृष्टिकोण से, यदि आप इस पर ताला लगाते हैं, तो आपको हमेशा दो प्रश्नों के उत्तर देने होंगे:

  1. क्या पहुँच की तरह द्वारा सुरक्षित है यह ?
  2. क्या एक ताला वास्तव में पर्याप्त है, किसी ने बग का परिचय नहीं दिया?

एक उदाहरण:

class BadObject {
    private Something mStuff;
    synchronized setStuff(Something stuff) {
        mStuff = stuff;
    }
    synchronized getStuff(Something stuff) {
        return mStuff;
    }
    private MyListener myListener = new MyListener() {
        public void onMyEvent(...) {
            setStuff(...);
        }
    }
    synchronized void longOperation(MyListener l) {
        ...
        l.onMyEvent(...);
        ...
    }
}

यदि दो धागे longOperation()दो अलग-अलग उदाहरणों पर शुरू होते हैं BadObject, तो वे अपने ताले प्राप्त करते हैं; जब आह्वान करने का समय होता है l.onMyEvent(...), तो हमारे पास एक गतिरोध होता है क्योंकि दोनों में से कोई भी धागा अन्य वस्तु के लॉक को प्राप्त नहीं कर सकता है।

इस उदाहरण में हम दो तालों का उपयोग करके गतिरोध को समाप्त कर सकते हैं, एक लघु परिचालन के लिए और एक लंबे समय के लिए।


2
इस उदाहरण में एक गतिरोध प्राप्त करने का एकमात्र तरीका है जब BadObjectA longOperation, B पर आक्रमण करता है, A के गुजरता है myListener, और इसके विपरीत। असंभव नहीं है, लेकिन काफी जटिल है, मेरे पहले के बिंदुओं का समर्थन करता है।
eljenso

1

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

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

अंतर का अधिक विस्तृत विवरण आप यहां पा सकते हैं: http://www.artima.com/insidejvm/ed2/threadsynchP.html

निम्नलिखित बिंदुओं के कारण सिंक्रनाइज़ ब्लॉक का उपयोग अच्छा नहीं है:

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

इस क्षेत्र में अधिक जानकारी के लिए मैं आपको इस लेख को पढ़ने की सलाह दूंगा : http://java.dzone.com/articles/synchronized-considered


1

यह वास्तव में अन्य उत्तरों के लिए केवल पूरक है, लेकिन यदि लॉकिंग के लिए निजी वस्तुओं का उपयोग करने के लिए आपकी मुख्य आपत्ति यह है कि यह आपकी कक्षा को उन फ़ील्ड्स के साथ बंद कर देता है जो व्यावसायिक तर्क से संबंधित नहीं हैं, तो प्रोजेक्ट लंबोक को @Synchronizedसंकलन-समय पर बॉयलरप्लेट उत्पन्न करना होगा:

@Synchronized
public int foo() {
    return 0;
}

के लिए संकलित करता है

private final Object $lock = new Object[0];

public int foo() {
    synchronized($lock) {
        return 0;
    }
}

0

सिंक्रनाइज़ (यह) के उपयोग के लिए एक अच्छा उदाहरण।

// add listener
public final synchronized void addListener(IListener l) {listeners.add(l);}
// remove listener
public final synchronized void removeListener(IListener l) {listeners.remove(l);}
// routine that raise events
public void run() {
   // some code here...
   Set ls;
   synchronized(this) {
      ls = listeners.clone();
   }
   for (IListener l : ls) { l.processEvent(event); }
   // some code here...
}

जैसा कि आप यहां देख सकते हैं, हम वहां पर कुछ सिंक्रनाइज़ विधियों के साथ लंबे समय तक (संभवतः रन विधि के अनंत लूप) के आसान सहयोग के लिए इस पर सिंक्रनाइज़ का उपयोग करते हैं।

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


किसी भी वस्तु को यहां लॉक के रूप में इस्तेमाल किया जा सकता है। यह होने की जरूरत नहीं है this। यह एक निजी क्षेत्र हो सकता है।
फाइनव

सही है, लेकिन इस उदाहरण का उद्देश्य यह दिखाना था कि हम उचित सिंक्रनाइज़ेशन कैसे करते हैं, अगर हमने विधि सिंक्रनाइज़ेशन का उपयोग करने का निर्णय लिया है।
बार्ट प्रकोप

0

यह उस कार्य पर निर्भर करता है जिसे आप करना चाहते हैं, लेकिन मैं इसका उपयोग नहीं करूंगा। इसके अलावा, जाँच करें कि क्या थ्रेड-सेव-नेस को आप साथ रखना चाहते हैं, पहले स्थान पर सिंक्रोनाइज़ (यह) द्वारा नहीं किया जा सकता है? एपीआई में कुछ अच्छे ताले भी हैं जो आपकी मदद कर सकते हैं :)


0

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

// Synchronization objects (locks)
private static HashMap<String, Object> locks = new HashMap<String, Object>();
// Simple method
private static Object atomic() {
    StackTraceElement [] stack = Thread.currentThread().getStackTrace(); // get execution point 
    StackTraceElement exepoint = stack[2];
    // creates unique key from class name and line number using execution point
    String key = String.format("%s#%d", exepoint.getClassName(), exepoint.getLineNumber()); 
    Object lock = locks.get(key); // use old or create new lock
    if (lock == null) {
        lock = new Object();
        locks.put(key, lock);
    }
    return lock; // return reference to lock
}
// Synchronized code
void dosomething1() {
    // start commands
    synchronized (atomic()) {
        // atomic commands 1
        ...
    }
    // other command
}
// Synchronized code
void dosomething2() {
    // start commands
    synchronized (atomic()) {
        // atomic commands 2
        ...
    }
    // other command
}

0

synchronized(this)लॉकिंग तंत्र के रूप में उपयोग करने से बचें : यह पूरी कक्षा के उदाहरण को लॉक करता है और गतिरोध पैदा कर सकता है। ऐसे मामलों में, कोड को केवल एक विशिष्ट विधि या चर को बंद करने के लिए रिफलेक्टर किया जाता है, इस तरह से पूरी कक्षा लॉक नहीं होती है। Synchronisedविधि स्तर के अंदर इस्तेमाल किया जा सकता है।
उपयोग करने के बजाय synchronized(this), नीचे दिए गए कोड से पता चलता है कि आप कैसे एक विधि को बंद कर सकते हैं।

   public void foo() {
if(operation = null) {
    synchronized(foo) { 
if (operation == null) {
 // enter your code that this method has to handle...
          }
        }
      }
    }

0

2019 में मेरे दो सेंट भले ही यह सवाल पहले से ही सुलझाए जा सकते थे।

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

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

आगे विस्तार से बताने के लिए, यदि आप एक गैर-सुलभ लॉक पर लॉक करते हैं (कोई भी आपका लॉक चोरी नहीं कर सकता है), तो आप कुल नियंत्रण में हैं और इसके विपरीत, आपको पता होना चाहिए कि आप क्या कर रहे हैं। ..)।

मेरे लिए समस्या यह है कि विधि परिभाषा हस्ताक्षर में सिंक्रोनाइज़ किया गया कीवर्ड प्रोग्रामर के लिए इतना आसान है कि वह सोच भी नहीं सकता पर क्या ताला लगाना है, यह सोचने के लिए एक शक्तिशाली महत्वपूर्ण बात है कि क्या आप एक बहु में समस्याओं में भागना नहीं चाहते हैं। -विस्तृत कार्यक्रम

कोई यह तर्क नहीं दे सकता है कि 'आम तौर पर' आप अपने वर्ग के उपयोगकर्ताओं को इन सामानों को करने में सक्षम नहीं होते हैं या 'आमतौर पर' जो आप चाहते हैं ... यह निर्भर करता है कि आप किस कार्यक्षमता पर कोडिंग कर रहे हैं। आप सभी उपयोग के मामलों की भविष्यवाणी नहीं कर सकते क्योंकि आप एक अंगूठे का नियम नहीं बना सकते।

उदाहरण के लिए विचार करें कि प्रिंटराइटर जो एक आंतरिक लॉक का उपयोग करता है लेकिन फिर लोग इसे कई थ्रेड से उपयोग करने के लिए संघर्ष करते हैं यदि वे अपने आउटपुट को इंटरलेव नहीं करना चाहते हैं।

क्या क्लास के बाहर आपका ताला सुलभ होना चाहिए या नहीं, यह प्रोग्रामर के रूप में आपका निर्णय है कि कक्षा में क्या कार्यक्षमता है। यह एपीआई का हिस्सा है। उदाहरण के लिए आप कोड का उपयोग करके कोड में परिवर्तन तोड़कर बिना इसे सिंक्रनाइज़ (provateObjet) से सिंक्रनाइज़ (यह) से दूर नहीं जा सकते।

नोट 1: मुझे पता है कि आप एक स्पष्ट लॉक ऑब्जेक्ट का उपयोग करके और इसे उजागर करने के लिए जो कुछ भी सिंक्रनाइज़ (यह) 'प्राप्त' कर सकते हैं, लेकिन मुझे लगता है कि यह अनावश्यक है अगर आपका व्यवहार अच्छी तरह से प्रलेखित है और आपको वास्तव में पता है कि 'इस' का मतलब क्या है।

नोट 2: मैं इस तर्क से सहमत नहीं हूं कि अगर कुछ कोड गलती से आपके लॉक को एक बग चुरा रहा है और आपको इसे हल करना है। यह एक तरह से तर्क है कि मैं अपने सभी तरीकों को सार्वजनिक कर सकता हूं, भले ही वे सार्वजनिक न हों। अगर कोई 'गलती से' कह रहा है कि मेरा इरादा निजी तरीके से इसका बग है। इस दुर्घटना को पहले स्थान पर क्यों सक्षम करें !!! यदि आपके लॉक को चुराने की क्षमता आपके वर्ग के लिए एक समस्या है, तो इसे अनुमति न दें। कि जैसे ही आसान।


-3

मुझे लगता है कि अंक एक (आपके लॉक का उपयोग करने वाला कोई और) और दो (एक ही लॉक का अनावश्यक रूप से उपयोग करने वाले सभी तरीके) किसी भी बड़े आवेदन में हो सकते हैं। खासकर जब डेवलपर्स के बीच कोई अच्छा संचार नहीं है।

यह पत्थर में नहीं डाला गया है, यह ज्यादातर अच्छे अभ्यास और त्रुटियों को रोकने का मुद्दा है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.