2011-01-21 8 views
5

मैं, जावालूप तुल्यकालन गतिरोध

public class Counter { 
    private int value; 

    public Counter(int value) { 
     this.value = value; 
    } 
    public void setValue(int value) { 
     this.value = value; 
    } 
    public void decrement() { 
     this.value--; 
    } 
    public int getValue() { 
     return this.value; 
    } 
} 

public class Cell extends Thread { 

    private Object sync; 
    private Counter counter; 

    public Cell(Object sync, Counter counter) { 
     this.sync = sync; 
     this.counter = counter; 
    } 

    public void run() { 
     for (int r=0; r<Simulation.ROUND_NUM; r++) { 

      // do something 

      synchronized(counter) { 
       counter.decrement(); 
       counter.notifyAll(); 
      } 
      synchronized(sync) { 
       try { 
        sync.wait(); 
       } 
       catch (Exception ex) {} 
      } 

     } 
    } 
} 

public class Simulation extends Thread { 

    public static final int THREAD_NUM = 5; 
    public static final int ROUND_NUM = 5; 

    public Object sync = new Object(); 
    private Counter counter = new Counter(THREAD_NUM); 

    public void run() { 

     for (int i=0; i<THREAD_NUM; i++) { 
      Cell c = new Cell(sync,counter); 
      c.start(); 
     } 

     for (int i=0; i<ROUND_NUM; i++) { 
      synchronized(counter) { 
       while(counter.getValue() != 0) { 
        try { 
         counter.wait(); 
        } 
        catch (Exception ex) {} 
       } 
       counter.setValue(THREAD_NUM); 
      } 

      synchronized(sync) { 
       sync.notifyAll(); 
      } 
     } 
    } 
} 

उद्देश्य प्रत्येक सेल थ्रेड में पाश की अगले चरण को क्रियान्वित करने से रोकने के लिए है में निम्नलिखित वर्गों मिल गया है जब तक हर सेल थ्रेड प्रत्येक यात्रा पर किया जाएगा। मेरा समाधान कभी-कभी डेडलॉक की ओर जाता है। मैं समझ नहीं पा रहा हूं क्यों। कृपया

+2

साइड नोट: यदि आपको स्पष्ट रूप से 'थ्रेड',' प्रतीक्षा 'और' सूचित करें '(और आप जावा 5 या बाद में उपयोग कर रहे हैं) का उपयोग करने की आवश्यकता नहीं है, तो आप एक ['CountDownLatch'] (http से बेहतर होंगे) : //download.oracle.com/javase/6/docs/api/java/util/concurrent/CountDownLatch.html) इसके बजाए। –

उत्तर

3

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

चूंकि वह अंतिम सेल इंतजार नहीं कर रहा है, इसलिए यह अधिसूचित नहीं किया जाता है इसलिए पूरी चीज फंस जाती है। आप प्रत्येक synchronized (sync) ब्लॉक में पहली पंक्ति के रूप में System.out.println() को जोड़कर और "सिंक के लिए प्रतीक्षा" और "सिंक को अधिसूचित" लिखकर लिख सकते हैं। आप देखेंगे कि सिंक के लिए केवल 4 थ्रेड सिंक की प्रतीक्षा कर रहे हैं जब आप इसे सूचित करते हैं।

यकीन है कि हर किसी को बनाने के लिए इंतज़ार कर रहा है जब सिम्युलेटर सूचित करता है, Cell#run() में दो सिंक्रनाइज़ ब्लॉक नेस्ट है:

public class Counter { 
    private int value; 

    public Counter(int value) { 
     this.value = value; 
    } 

    public void setValue(int value) { 
     this.value = value; 
    } 

    public void decrement() { 
     this.value--; 
    } 

    public int getValue() { 
     return this.value; 
    } 

    public static void main(String[] args) { 
     new Simulation().start(); 
    } 
} 

class Cell extends Thread { 

    private Object sync; 
    private Counter counter; 

    public Cell(Object sync, Counter counter) { 
     this.sync = sync; 
     this.counter = counter; 
    } 

    public void run() { 
     for (int r = 0; r < Simulation.ROUND_NUM; r++) { 

      // do something 

      synchronized (sync) { 
       synchronized (counter) { 
        counter.decrement(); 
        counter.notifyAll(); 
       } 
       try { 
        sync.wait(); 
       } catch (Exception ignored) {} 
      } 


     } 
    } 
} 

class Simulation extends Thread { 

    public static final int THREAD_NUM = 900; 
    public static final int ROUND_NUM = 30; 

    public Object sync = new Object(); 
    private Counter counter = new Counter(THREAD_NUM); 

    public void run() { 

     for (int i = 0; i < THREAD_NUM; i++) { 
      Cell c = new Cell(sync, counter); 
      c.start(); 
     } 

     for (int i = 0; i < ROUND_NUM; i++) { 
      synchronized (counter) { 
       while (counter.getValue() != 0) { 
        try { 
         counter.wait(); 
        } catch (Exception ex) { 
        } 
       } 
       counter.setValue(THREAD_NUM); 
      } 

      synchronized (sync) { 
       sync.notifyAll(); 
      } 
     } 
    } 
} 
+0

यह निश्चित रूप से बेहतर काम करता है, लेकिन फिर भी मैं जितना अधिक धागा बनाता हूं, उतना अधूरा प्रोग्राम मुझे मिलता है। 900 धागे और 30 पुनरावृत्तियों को ध्यान में रखते हुए - मेरे 10 हालिया परीक्षणों में से केवल 2 सफलतापूर्वक समाप्त हो गए थे। – marooou

+0

मुझे नहीं लगता कि आप अब डेडलॉक्स क्यों प्राप्त करेंगे ... क्या वह कोड है जिसे आपने सही कोड पोस्ट किया है जिसे आप चला रहे हैं? –

+0

यह सही कोड नहीं है। मेरे कोड में टिप्पणी कुछ कार्रवाइयों के साथ बदल दी गई है, लेकिन जब मैं सेल क्लास में उन सभी को हटा देता हूं, तो समस्या दूर नहीं जाती है। और जब भी मैं टिप्पणी के बजाय नींद डालता हूं, तो कार्यक्रम भी पूरी तरह से काम करता है ... हमेशा। – marooou

5

सहायता करें सबसे पहले आप काउंटर क्लास के बजाय AtomicInteger कक्षा का उपयोग कर सकते हैं। परमाणु इंटेगर वर्ग थ्रेड-सुरक्षित है ताकि आप परमाणु क्रिया जैसे कमी और गेट और वृद्धि और गेट का उपयोग कर सकें।

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

2

आपका कोड गतिरोध कर सकते हैं क्योंकि आप किसी भी गारंटी नहीं कर रहे हैं कि सेल सूत्र वास्तव में हो जाएगा उस समय प्रतीक्षा करें() ब्लॉक जो अधिसूचित करता है सभी होता है। निम्नलिखित घटनाओं का एक अनुक्रम है जो इस समस्या का कारण बन सकता है:

  1. सिमुलेशन सभी धागे शुरू करता है, और 0 मान के लिए प्रतीक्षा करने वाले ब्लॉक।
  2. अनुक्रम में प्रत्येक धागा घटती कहता है, तो counter.notifyAll, और फिर खो देता है अपने समय टुकड़ा
  3. मुख्य थ्रेड अधिसूचित किया गया है, जाग, पाता काउंटर, sync.notifyAll कॉल 0 पर है, शीर्ष करने के लिए लूप , और अनिश्चित काल तक इंतजार कर रहा है।
  4. अनुक्रम में प्रत्येक थ्रेड को समय स्लाइस दिया जाता है, प्रतीक्षा() के लिए अग्रिम, और अनिश्चित काल तक प्रतीक्षा करता है।
0

लवली उदाहरण! यह डेडलॉक नहीं है, क्योंकि परिभाषा के अनुसार केवल तभी हो सकता है जब एक थ्रेड एक साथ एक से अधिक ताला रखता है, और दूसरा एक ही क्रम में एक ही ताले को हासिल करने का प्रयास करता है।
मुझे संदेह है कि यहां समस्याएं सेल ऑब्जेक्ट्स में होने वाली नकली जागरूकता के कारण होती हैं (यदि सिमुलेशन ऑब्जेक्ट में एक नकली जागरूकता उत्पन्न होती है तो इसका कोई प्रभाव नहीं पड़ता है क्योंकि लूप में प्रतीक्षा() को कॉल किया जाता है पुनः प्रवेश करने का इंतजार)।
सेल में एक नकली जागने से अतिरिक्त कमी आएगी। यह बदले में while(counter.getValue() != 0) परीक्षण करने के लिए परीक्षण करेगा।
उस स्थिति को while(counter.getValue() >= 0) पर बदलें और 'डेडलॉक्स गायब हो जाना चाहिए। अगर यह काम करता है तो कृपया हमें बताएं।

+0

नहीं, मुझे ऐसा नहीं लगता है। मुझे लगता है कि वह किसी भी तरह की अधिसूचना को याद करता है, मुझे समझ में नहीं आता कि कैसे। लेकिन जब यह अटक जाता है तो काउंटर अभी भी 0 पर है। –

+0

यह मेरा पहला विचार था, लेकिन एमके सही है। यह चाल नहीं करता है। – marooou

+0

मैंने कोशिश की, लेकिन आजीविका नहीं हो सका। –

0

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