यदि मैं एक ही कक्षा में दो विधियों को सिंक्रनाइज़ करता हूं, तो क्या वे एक साथ चल सकते हैं?


164

यदि मैं एक ही वर्ग पर दो विधियों को सिंक्रनाइज़ करता हूं, तो क्या वे एक ही वस्तु पर एक साथ चल सकते हैं ? उदाहरण के लिए:

class A {
    public synchronized void methodA() {
        //method A
    }

    public synchronized void methodB() {
        // method B
    }
}

मुझे पता है कि मैं methodA()दो अलग-अलग धागों में एक ही वस्तु पर दो बार नहीं चल सकता । में एक ही बात methodB()

लेकिन क्या मैं अभी भी methodB()अलग थ्रेड पर चल सकता हूं methodA()? (एक ही वस्तु)

जवाबों:


148

दोनों विधियाँ एक ही मॉनिटर को लॉक करती हैं। इसलिए, आप एक साथ अलग-अलग थ्रेड्स से एक ही ऑब्जेक्ट पर उन्हें निष्पादित नहीं कर सकते हैं (दो तरीकों में से एक तब तक ब्लॉक होगा जब तक कि दूसरा समाप्त नहीं हो जाता)।


1
इस सवाल पर मेरा एक ऐड था। मान लीजिए कि दोनों विधि स्थिर हैं, अब पद्धति को क्लास का उपयोग करना कहा जाता है जबकि मेथोड को ए 1 में ऑब्जेक्ट ए। एमथोडा () और टी 2 में obj.methodB () की तरह उपयोग किया जाता है। अब क्या होगा, क्या वे ब्लॉक करेंगे ????
आमोद

2
@ amod0017: कब का obj.methodB()पर्याय है । इसलिए हाँ, वे ब्लॉक करेंगे (वर्ग के आधार पर, वस्तु के नहीं, मॉनिटर)। A.methodB()methodB()static
एनपीई

कोशिश करेंगे और इसे वापस पाने के लिए। :)
आमोद

@NPE तो भले ही दोनों विधि स्थिर हों और एक ही ऑब्जेक्ट पर 2 थ्रेड्स t1 और t2 दोनों मेथड () और मेथडबी () को एक साथ कॉल करने की कोशिश करते हैं, तब केवल 1 (t t1) थ्रेड निष्पादित होता है और दूसरे थ्रेड को तब तक इंतजार करना पड़ता है जब तक कि t रिलीज़ लॉक न हो जाए। ?
श्रीप्रसाद

8
ध्यान रखें कि स्थैतिक विधियाँ .classऑब्जेक्ट पर लॉक का उपयोग करती हैं । तो अगर आपके पास है class A {static synchronized void m() {} }। और फिर एक धागा कॉल new A().m()करता है जो new A()ऑब्जेक्ट पर लॉक का अधिग्रहण करता है । यदि फिर एक और धागा A.m()इसे ENTERS METHOD NO PROBLEM कहता है क्योंकि जो दिखता है वह A.classऑब्जेक्ट पर लॉक होता है जबकि NO THREADS में इस तरह का लॉक होता है । भले ही आपने विधि घोषित कर दी हो, लेकिन synchronizedयह समान समय पर दो अलग-अलग थ्रेड द्वारा पहुँचा जा सकता है । इस प्रकार: स्थैतिक तरीकों को कॉल करने के लिए कभी भी ऑब्जेक्ट रेफरेंस का उपयोग न करें
एलेक्स सेमीनीक

113

उदाहरण में मेथड और मेथड बी उदाहरण विधियाँ हैं (जैसा कि स्टैटिक विधियों के विपरीत)। synchronizedइंस्टेंस मेथड पर डालने का मतलब है कि थ्रेड को उस ऑब्जेक्ट पर किसी भी कोड को निष्पादित करना शुरू करने से पहले जिस ऑब्जेक्ट पर कॉल किया जाता है, उस ऑब्जेक्ट पर लॉक ("आंतरिक लॉक") प्राप्त करना होगा।

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

दो तरीकों को समवर्ती रूप से चलाने के लिए उन्हें अलग-अलग तालों का उपयोग करना होगा, जैसे:

class A {
    private final Object lockA = new Object();
    private final Object lockB = new Object();

    public void methodA() {
        synchronized(lockA) {
            //method A
        }
    }

    public void methodB() {
        synchronized(lockB) {
            //method B
        }
    }
}

जहाँ सिंक्रोनाइज़्ड ब्लॉक सिंटैक्स एक विशिष्ट ऑब्जेक्ट को निर्दिष्ट करने की अनुमति देता है जिसे ब्लॉक में प्रवेश करने के लिए निष्पादित थ्रेड को आंतरिक लॉक प्राप्त करने की आवश्यकता होती है।

समझने की महत्वपूर्ण बात यह है कि भले ही हम अलग-अलग तरीकों पर "सिंक्रनाइज़" कीवर्ड डाल रहे हैं, लेकिन मूल अवधारणा पर्दे के पीछे आंतरिक ताला है।

यहां बताया गया है कि जावा ट्यूटोरियल रिलेशनशिप के बारे में बताता है:

आंतरिक आंतरिक लॉक या मॉनिटर लॉक के रूप में जानी जाने वाली आंतरिक इकाई के आसपास सिंक्रोनाइज़ेशन का निर्माण किया जाता है। (एपीआई विनिर्देश अक्सर इस इकाई को केवल "मॉनिटर" के रूप में संदर्भित करता है) आंतरिक ताले सिंक्रनाइज़ेशन के दोनों पहलुओं में एक भूमिका निभाते हैं: किसी वस्तु की स्थिति के लिए अनन्य पहुंच को लागू करना और ऐसा होना चाहिए-पहले रिश्ते जो दृश्यता के लिए आवश्यक हैं।

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

लॉकिंग का उद्देश्य साझा डेटा की रक्षा करना है। आप अलग-अलग ताले का उपयोग करेंगे जैसा कि ऊपर दिए गए उदाहरण कोड में दिखाया गया है यदि प्रत्येक लॉक अलग-अलग डेटा सदस्यों को सुरक्षित रखता है।


इसलिए इस उदाहरण में ताला LockA \ LockB ऑब्जेक्ट्स पर है और क्लास A पर नहीं है? क्या यह एक क्लास लेवल लॉकिंग उदाहरण है?
निम्रोद

2
@ नीमरॉड: यह लॉकए पर और लॉकबी ऑब्जेक्ट्स पर लॉकिंग है और ए के उदाहरण पर नहीं है। यहां कुछ भी एक क्लास पर लॉक नहीं है। क्लास-लेवल लॉकिंग का मतलब होगा क्लास ऑब्जेक्ट पर लॉक प्राप्त करना, जैसे कुछ का उपयोग करना static synchronizedयाsynchronized (A.class)
नाथन ह्यूजेस

यहाँ जावा ट्यूटोरियल का लिंक दिया गया है, जो यहाँ बताया गया है।
अल्बर्टो डे पाओला

18

जावा थ्रेड ऑब्जेक्ट लेवल लॉक को तब प्राप्त करता है जब यह एक उदाहरण सिंक्रनाइज़ जावा विधि में प्रवेश करता है और एक क्लास लेवल लॉक प्राप्त करता है जब यह स्थिर सिंक्रनाइज़ जावा मोड में प्रवेश करता है ।

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


यदि मेरे पास कक्षा के दो अलग-अलग उदाहरणों पर दो धागे हैं तो वे दोनों विधियों को एक साथ निष्पादित करने में सक्षम होंगे, जैसे कि एक थ्रेड एक सिंक्रनाइज़ किए गए तरीके को कहता है और दूसरा एक दूसरे को सिंक्रनाइज़ किए गए तरीके से कॉल करता है। अगर मेरी समझ सही है, तो क्या मैं private final Object lock = new object();केवल एक ही सूत्र को निष्पादित करने में सक्षम होने के लिए सिंक्रनाइज़ के साथ उपयोग कर सकता हूं ? धन्यवाद
युग सिंह

13

आपके मामले में आपने कक्षा के एक ही उदाहरण पर दो विधि को सिंक्रनाइज़ किया है। इसलिए, ये दोनों विधियां एक ही वर्ग ए के उदाहरण के विभिन्न थ्रेड पर एक साथ नहीं चल सकती हैं, लेकिन वे अलग-अलग वर्ग ए के उदाहरणों पर कर सकते हैं।

class A {
    public synchronized void methodA() {
        //method A
    }
}

के समान है:

class A {
    public void methodA() {
        synchronized(this){
            // code of method A
        }
    }
}

क्या होगा अगर मैं एक लॉक को परिभाषित करता हूं private final Object lock = new Object();और अब lockदो तरीकों से सिंक्रनाइज़ ब्लॉक के साथ उपयोग करता हूं तो क्या आपका कथन सही होगा? IMO चूंकि ऑब्जेक्ट सभी ऑब्जेक्ट्स का पैरेंट क्लास है, भले ही थ्रेड्स क्लास के अलग-अलग उदाहरणों पर हों, एक बार में केवल एक ही ब्लॉक के अंदर कोड को एक्सेस कर सकता है। धन्यवाद।
युग सिंह

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

हां, ऑब्जेक्ट सभी वर्गों के लिए एक अभिभावक है, लेकिन आपके मामले में "लॉक" उदाहरण "प्रति वर्ग का उदाहरण" है, इसलिए, यह सिंक्रनाइज़ेशन के लिए "यह" के समान प्रभाव है।
ओलेकांदर_डज

7

ओरेकल प्रलेखन लिंक से

सिंक्रनाइज़ किए जा रहे तरीकों के दो प्रभाव हैं:

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

दूसरा, जब एक सिंक्रनाइज़ विधि बाहर निकलती है, तो यह स्वचालित रूप से एक ही ऑब्जेक्ट के लिए एक सिंक्रनाइज़ किए गए विधि के किसी भी बाद के आह्वान के साथ एक-पहले संबंध स्थापित करता है। यह गारंटी देता है कि ऑब्जेक्ट की स्थिति में परिवर्तन सभी थ्रेड्स को दिखाई देते हैं

यह आपके प्रश्न का उत्तर देगा: एक ही ऑब्जेक्ट पर, आप दूसरी सिंक्रनाइज़ किए गए तरीके को कॉल नहीं कर सकते हैं जब पहले सिंक्रनाइज़ विधि निष्पादन प्रगति पर है।

आंतरिक लॉक और लॉक व्यवहार को समझने के लिए इस दस्तावेज़ पृष्ठ पर एक नज़र डालें ।


6

नीचे के रूप में अपने कोड के बारे में सोचो:

class A {

public void methodA() {
    synchronized(this){        
      //method A body
    }
}

public void methodB() {
    synchronized(this){
      // method B body
    }
}

तो, विधि के स्तर पर सिंक्रनाइज़ का मतलब केवल सिंक्रनाइज़ (यह) है। यदि कोई धागा इस वर्ग की एक विधि चलाता है, तो यह निष्पादन शुरू करने से पहले ताला प्राप्त करेगा और विधि के निष्पादन समाप्त होने तक इसे दबाए रखेगा।

लेकिन क्या मैं मेथडब () को विभिन्न थ्रेड पर चला सकता हूं जबकि मेथड () अभी भी चल रहा है? (एक ही वस्तु)

वास्तव में, यह संभव नहीं है!

इसलिए, कई थ्रेड्स एक ही ऑब्जेक्ट पर एक साथ कई सिंक्रनाइज़ किए गए तरीकों को चलाने में सक्षम नहीं होंगे।


क्या होगा यदि मैं एक ही कक्षा की दो अलग-अलग वस्तुओं पर थ्रेड्स बनाता हूं? इस स्थिति में, यदि मैं एक थ्रेड से एक विधि और दूसरे थ्रेड से अन्य विधि कहता हूं तो क्या वे एक साथ निष्पादित नहीं होंगे?
युग सिंह

2
वे क्योंकि वे अलग-अलग वस्तुएं हैं। यही है, यदि आप इसे रोकना चाहते हैं, तो आप स्थैतिक तरीकों का उपयोग कर सकते हैं और वर्ग को सिंक्रनाइज़ कर सकते हैं या एक वर्ग चर वस्तु को लॉक के रूप में उपयोग कर सकते हैं या वर्ग सिंगलटन बना सकते हैं। @ युग सिंह
खोसरो मकर

4

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


3

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

नमूना कार्यक्रम के नीचे स्पष्ट रूप से समान इंगित करना है -

public class Test {

public synchronized void methodA(String currentObjectName) throws InterruptedException {
    System.out.println(Thread.currentThread().getName() + "->" +currentObjectName + "->methodA in");
    Thread.sleep(1000);
    System.out.println(Thread.currentThread().getName() + "->" +currentObjectName + "->methodA out");
}

public synchronized void methodB(String currentObjectName)  throws InterruptedException {
    System.out.println(Thread.currentThread().getName() + "->" +currentObjectName + "->methodB in");
    Thread.sleep(1000);
    System.out.println(Thread.currentThread().getName() + "->" +currentObjectName + "->methodB out");
}

public static void main(String[] args){
    Test object1 = new Test();
    Test object2 = new Test();
    //passing object instances to the runnable to make calls later
    TestRunner runner = new TestRunner(object1,object2);
    // you need to start atleast two threads to properly see the behaviour
    Thread thread1 = new Thread(runner);
    thread1.start();
    Thread thread2 = new Thread(runner);
    thread2.start();
}
}

class TestRunner implements Runnable {
Test object1;
Test object2;

public TestRunner(Test h1,Test h2) {
    this.object1 = h1;
    this.object2 = h2;
}

@Override
public void run() {
    synchronizedEffectiveAsMethodsCalledOnSameObject(object1);
    //noEffectOfSynchronizedAsMethodsCalledOnDifferentObjects(object1,object2);
}

// this method calls the method A and B with same object instance object1 hence simultaneous NOT possible
private void synchronizedEffectiveAsMethodsCalledOnSameObject(Test object1) {
    try {
        object1.methodA("object1");
        object1.methodB("object1");
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

// this method calls the method A and B with different object instances object1 and object2 hence simultaneous IS possible
private void noEffectOfSynchronizedAsMethodsCalledOnDifferentObjects(Test object1,Test object2) {
    try {
        object1.methodA("object1");
        object2.methodB("object2");
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}
}

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

Ouput with noEffectOfSynchronizedAsMethodsCalledOnDifferentObjects () टिप्पणी -आउटपुट आउटपुट मेथड मे है Ouput के साथ * noEffectOfSynchronizedAsMethodsCalledOnDifferentObjects () * टिप्पणी की

और साथ ouput synchronizedEffectiveAsMethodsCalledOnSameObject () टिप्पणी की - उत्पादन शो एक साथ प्रकाश डाला खंड में Thread1 और Thread0 द्वारा MethodA का उपयोग -

Ouput के साथ * synchronEffectiveAsMethodsCalledOnSameObject () * टिप्पणी की

थ्रेड्स की संख्या बढ़ाने से यह और भी अधिक ध्यान देने योग्य हो जाएगा।


2

नहीं, यह संभव नहीं है, यदि यह संभव था तो दोनों विधि एक ही चर को एक साथ अद्यतन कर सकती है जो डेटा को आसानी से भ्रष्ट कर सकती है।


2

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


1

आप इसे क्लास में नहीं ऑब्जेक्ट पर सिंक्रोनाइज़ कर रहे हैं। इसलिए वे एक ही वस्तु पर एक साथ नहीं चल सकते


0

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


1
कृपया इस गड़बड़ी को ठीक से रोकें और कैपिटल करें। No वैरिफाई ’जैसा कोई शब्द नहीं है।
लोर्ने
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.