2014-10-07 7 views
5
public class MyStack2 { 
    private int[] values = new int[10]; 
    private int index = 0; 

    public synchronized void push(int x) { 
     if (index <= 9) { 
      values[index] = x; 
      Thread.yield(); 
      index++; 
     } 
    } 

    public synchronized int pop() { 
     if (index > 0) { 
      index--; 
      return values[index]; 
     } else { 
      return -1; 
     } 
    } 

    public synchronized String toString() { 
     String reply = ""; 
     for (int i = 0; i < values.length; i++) { 
      reply += values[i] + " "; 
     } 
     return reply; 
    } 
} 

public class Pusher extends Thread { 
    private MyStack2 stack; 

    public Pusher(MyStack2 stack) { 
     this.stack = stack; 
    } 

    public void run() { 
     for (int i = 1; i <= 5; i++) { 
      stack.push(i); 
     } 
    } 
} 

public class Test { 
    public static void main(String args[]) { 
     MyStack2 stack = new MyStack2(); 
     Pusher one = new Pusher(stack); 
     Pusher two = new Pusher(stack); 
     one.start(); 
     two.start(); 
     try { 
      one.join(); 
      two.join(); 
     } catch (InterruptedException e) { 
     } 
     System.out.println(stack.toString()); 
    } 
} 

MyStack2 वर्ग के तरीकों के बाद से पर अवरोध खोल करता सिंक्रनाइज़ किए जाते हैं, मैं 1 2 3 4 5 1 2 3 4 5. के रूप में उत्पादन उम्मीद कर रहा था लेकिन उत्पादन होता है अनिश्चित। अक्सर यह देता है: 1 1 2 2 3 3 4 4 5 5थ्रेड synchronization- जब एक धागा एक वस्तु

मेरी समझ के अनुसार, जब धागा शुरू होता है तो यह push विधि पर लॉक प्राप्त करता है। push() के अंदर थ्रेड एक समय के लिए उपज। लेकिन क्या यह yield() कहलाता है जब यह ताला जारी करता है? अब जब धागा दो शुरू हो गया है, तो क्या थ्रेड दो लॉक प्राप्त करेगा इससे पहले कि कोई निष्पादन पूरा करेगा? क्या कोई बता सकता है कि थ्रेड एक स्टैक ऑब्जेक्ट पर लॉक कब जारी करता है?

+0

इस http://stackoverflow.com/questions/18635616/yield-inside-synchronized-block-lock-release-after-calling-yield – SimY4

उत्तर

5

synchronized विधि निष्पादित होने पर अन्य धागे को निष्पादित करने से रोक देगा। जैसे ही यह अन्य धागे लौटाता है (और अक्सर तुरंत) पहुंच प्राप्त कर सकता है।

  1. थ्रेड 1 कॉल push(1) और में अनुमति दी है
  2. थ्रेड 2 कॉल push(1) और जब थ्रेड 1 यह उपयोग कर रहा है अवरुद्ध है:

    परिदृश्य अपने 1 1 2 2 ... हो सकता है पाने के लिए।।

  3. थ्रेड 1 बाहर निकलता push(1)
  4. थ्रेड 2 लाभ push तक पहुंचता है और 1 को धक्का देता है लेकिन साथ ही थ्रेड 1 कॉल push(2) पर कॉल करता है।

परिणाम 1 1 2 - आप स्पष्ट रूप से देख सकते हैं कि यह कैसे जारी रहता है।

+0

हाँ पर एक नज़र डालें, लेकिन यदि ऐसा है तो प्रोग्राम का आउटपुट ऐसा नहीं होना चाहिए: 1 1 2 2 3 3 4 4 5 5. लेकिन अक्सर यह आउटपुट होता है। – sam

+7

@javaTech - कोई अनुबंध नहीं है जो कहता है कि जावा थ्रेड उचित होना चाहिए। लगभग कोई भी आदेश आपके कोड का परिणाम हो सकता है। 1 1 2 2 ... काफी मान्य है। – OldCurmudgeon

1

ऐसा लगता है कि सिंक्रनाइज़ और उपज कीवर्ड का अर्थ वास्तव में कुछ भ्रम हो सकता है।

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

यील्ड संकलक को सुझाव देता है (और हाँ यह केवल एक सुझाव है) कि वर्तमान धागा अपना आवंटित समय छोड़ सकता है और दूसरा धागा निष्पादन शुरू कर सकता है। हालांकि, यह हमेशा ऐसा नहीं होता है।

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

अप्रत्याशित व्यवहार उपज से आता है जैसा कि आपने अनुमान लगाया था निष्पादन समय नहीं छोड़ रहा था।

आशा है कि इससे मदद मिलेगी!

+0

"सिंक्रनाइज़ का मतलब है कि एक ही समय में केवल एक थ्रेड उस कोड ब्लॉक में प्रवेश कर सकता है।" यह केवल तभी सच है जब कोड हमेशा ब्लॉक में प्रवेश करते समय _same object_ को लॉक करने का प्रयास करता है। एक से अधिक धागे एक ही 'सिंक्रनाइज़ (foo) {...}' ब्लॉक में प्रवेश कर सकते हैं अगर 'foo' स्थिर नहीं है। –

+0

"यील्ड संकलक के लिए सुझाव देता है ..." कंपाइलर के लिए नहीं। कंपाइलर Thread.yield() और किसी अन्य विधि कॉल के बीच का अंतर नहीं जानता है। जादू (यदि कोई हो) रन-टाइम पर होता है, तो संभवतः जब JVM कार्यान्वयन में मूल धागा किसी प्रकार का "उपज" सिस्टम कॉल करता है। –

+2

"अप्रत्याशित व्यवहार उपज से नहीं निकलता है ..." मुझे ऐसा नहीं लगता क्योंकि, जैसा कि आपने बताया है, उपज() कॉल एक सिंक्रनाइज़ ब्लॉक के अंदर होती है। भले ही यह कुछ भी करता है या नहीं, अन्य थ्रेड उपज() रिटर्न से पहले नहीं चलेगा। अप्रत्याशितता इस तथ्य से आती है कि दो धागे एक ही ताला के लिए बार-बार विरोध कर रहे हैं। मान लें कि थ्रेड ए में लॉक है, और थ्रेड बी लॉक के लिए इंतजार कर रहा है। जब थ्रेड ए लॉक जारी करता है और फिर _immediately_ इसे फिर से लॉक करने का प्रयास करता है, तो कौन सा थ्रेड लॉक हो जाता है? _That_ अनुमानित नहीं है। –

2

आप जब कहते हैं:

मेरी समझ है, जब धागा भी इसे शुरू कर दिया है के अनुसार धक्का विधि पर एक ताला प्राप्त कर लेता है।

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

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

+1

"लॉकिंग डेटा संरचना को समवर्ती रूप से एक्सेस करने से रोकती है।" सावधान! नए लोग यह नहीं समझ सकते कि यह लॉकिंग _per se_ नहीं है जो डेटा की सुरक्षा करता है। उन्हें बताया जाना चाहिए (कभी-कभी एक से अधिक बार) जो डेटा की सुरक्षा करता है वह तथ्य यह है कि डेटा कोड अपडेट या उपयोग करने वाले कोड_ का हर ब्लॉक एक ही ताला लगा देता है। –

+0

@james: सहमत, reworded। –

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