2011-10-12 21 views
12

मैं निम्नलिखित जावा कोड है:डेडलॉक और अधिग्रहण (int)

import java.util.concurrent.*; 

class Foo{ 
    static Semaphore s = new Semaphore(1); 

    public void fun(final char c, final int r){ 
     new Thread(new Runnable(){ 
      public void run(){ 
       try{ 
        s.acquire(r); 
        System.out.println(c+"_"+r); 
        s.release(r+1); 
       } catch(Exception e){ e.printStackTrace(); } 
      } 
     }).start(); 
    } 
} 

class ths{ 
    public static void main(String[]args) throws Exception{ 
     Foo f = new Foo(); 
     f.fun('B',2); 
     f.fun('F',6); 
     f.fun('A',1); 
     f.fun('C',3); 
     f.fun('D',4); 
     f.fun('E',5); 
    } 
} 

आदर्श रूप में, इस आदेश और बाहर निकलने में F_6 के माध्यम से A_1 प्रिंट चाहिए, लेकिन किसी कारण से नहीं होता है कि । यह आमतौर पर ए_1 और बी 2 प्रिंट करता है और फिर यह अटक जाता है।

मुझे अपने कोड के साथ कुछ भी गलत नहीं मिल रहा है। कोई सुझाव?

उत्तर

7

मूल समस्या यह है कि acquire(int permits) गारंटी नहीं देता है कि सभी परमिट एक बार में पकड़े जाएंगे। यह कम परमिट प्राप्त कर सकता है और बाकी के इंतजार के दौरान ब्लॉक कर सकता है।

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

तुम इतनी तरह कोड को बदलते हैं, यह मेरे लिए समस्या का समाधान होता:

public void fun(final char c, final int r){ 
    new Thread(new Runnable(){ 
     public void run(){ 
      try{ 
       while (!s.tryAcquire(r, 1, TimeUnit.MILLISECONDS)) {}; 
       System.out.println(c+"_"+r); 
       s.release(r+1); 
      } catch(Exception e){ e.printStackTrace(); } 
     } 
    }).start(); 
} 

(दूसरा विचार पर, ऊपर भी टूट गया है तो कोई गारंटी नहीं होगा, क्योंकि सही धागा कभी परमिट प्राप्त करें - यह अनिश्चित काल तक कोशिश कर रहा है और समय निकाल सकता है।)

+0

यह ठीक करता है, लेकिन मुझे आश्चर्य है कि यह क्यों जरूरी है। यदि यह B_2 प्रिंट करने के बाद फंस गया है, और मुख्य थ्रेड में, कुछ समय बाद, मैं उपलब्ध परमिट की संख्या मुद्रित करता हूं, यह प्रिंट करता है 3. तो थ्रेड सी को जारी रखने की अनुमति क्यों नहीं है? – Vlad

+0

@Vlad: मेरा अनुमान है कि '3' इस तथ्य को प्रतिबिंबित नहीं करता है कि' डी' पहले से ही "अधिग्रहण (4) 'कॉल के लिए कुछ परमिट" आरक्षित "हो सकता है। – NPE

+0

मुझे लगता है कि आप सही हो सकते हैं। मैंने रिलीज के लिए पर्याप्त रूप से पर्याप्त दस्तावेज नहीं पढ़ा था। [कड़ी] (http://download.oracle.com/javase/6/docs/api/java/util/concurrent/Semaphore.html#release (int))। मैंने माना था कि कार्यान्वयन थ्रेड पर चक्र होगा और वास्तव में जागने वाले व्यक्ति को खोजने का प्रयास करेगा। – Vlad

0

Semaphore एक बार में सभी परमिट प्राप्त करता है, अन्यथा यह वास्तविक semaphore नहीं होगा। लेकिन: जावा संस्करण में भी एक आंतरिक प्रतीक्षा कतार है। और उस कतार का व्यवहार नहींवर्तमान में नि: शुल्क संसाधनों का सर्वोत्तम फिट प्रदान करता है लेकिन कतार में पहले के अनुरोध तक को अनुमति देने तक अधिक या कम परमिट इकट्ठा किया जा सकता है। लेकिन एक थ्रेड में प्रवेश करता है जो कि चेक पर किया जाता है यदि उपलब्ध परमिट थ्रेड को कतार में प्रवेश करने से बचने की अनुमति देता है। किसी को भी पहले कतार में प्रवेश करते हैं - 0 परमिट के साथ

  • प्रारंभ:

    import java.util.concurrent.*; 
    public class SemaphoreTest{ 
        static Semaphore s = new Semaphore(0); 
    
        public void fun(final char c, final int r) throws Exception { 
         new Thread(new Runnable(){ 
          public void run(){ 
           try{ 
            System.out.println("acquire "+r); 
            s.acquire(r); 
            System.out.println(c+"_"+r); 
           } catch(Exception e){ e.printStackTrace(); } 
          } 
         }).start(); 
         Thread.sleep(500); 
        } 
    
        public static void main(String[]args) throws Exception{ 
         SemaphoreTest f = new SemaphoreTest(); 
    
         f.fun('B',2); 
         f.fun('F',6); 
         f.fun('A',1); 
         f.fun('C',3); 
         f.fun('D',4); 
         f.fun('E',5); 
    
         while(s.hasQueuedThreads()){ 
          Thread.sleep(1000); 
          System.out.println("release "+1+", available "+(s.availablePermits()+1)); 
          s.release(1); 
         } 
        } 
    } 
    

    असल में निम्न परिवर्तन किया गया है:

    मुझे लगता है कि कतार व्यवहार दिखाने के लिए अपने को संशोधित किया है।

  • Thread.start के बाद प्रत्येक थ्रेड 500ms समय देकर कतार के क्रम को "परिभाषित करें"।
  • प्रत्येक थ्रेड acquire पर कॉल करेगा लेकिन release नहीं होगा।
  • मुख्य धागा दूसरे के बाद एक परमिट के साथ धीरे-धीरे सेमफोर को खिलाएगा।

    acquire 2 
    acquire 6 
    acquire 1 
    acquire 3 
    acquire 4 
    acquire 5 
    release 1, available 1 
    release 1, available 2 
    B_2 
    release 1, available 1 
    release 1, available 2 
    release 1, available 3 
    release 1, available 4 
    release 1, available 5 
    release 1, available 6 
    F_6 
    release 1, available 1 
    A_1 
    release 1, available 1 
    release 1, available 2 
    release 1, available 3 
    C_3 
    release 1, available 1 
    release 1, available 2 
    release 1, available 3 
    release 1, available 4 
    D_4 
    release 1, available 1 
    release 1, available 2 
    release 1, available 3 
    release 1, available 4 
    release 1, available 5 
    E_5 
    release 1, available 1 
    

    जिसका मतलब है:: प्रत्येक धागा awoken है, अगर

    • यह कतार के सिर पर है

    यह इस उत्पादन को निर्धारणात्मक दे देंगे।

  • पर्याप्त परमिट जमा किए गए हैं।
संबंधित मुद्दे