2016-05-02 7 views
6

प्राप्त किए बिना सेमफोर रिलीज मेरे पास थ्रेड हैं जिन्हें यादृच्छिक संख्या (1 से n) दिया जाता है और उन्हें क्रमबद्ध क्रम में मुद्रित करने का निर्देश दिया जाता है। मैंने सेमफोर का उपयोग किया जैसे कि मैं परमिट की संख्या = यादृच्छिक संख्या प्राप्त करता हूं और अधिग्रहित किए गए एक से अधिक परमिट जारी करता हूं।जावा -

अधिग्रहण = यादृच्छिक संख्या; जारी = 1 + यादृच्छिक संख्या

सेमफोर के लिए प्रारंभिक परमिट गणना 1 है। तो यादृच्छिक संख्या 1 के साथ धागा परमिट प्राप्त करना चाहिए और फिर 2 और इसी तरह।

यह कोई आवश्यकता है कि एक धागा है कि एक परमिट जारी अधिग्रहण को फोन करके कि परमिट हासिल कर ली है चाहिए() नहीं है नीचे

दिया प्रलेखन के अनुसार समर्थित है।

समस्या यह है कि मेरा प्रोग्राम n> 2 के लिए 1 के बाद अटक गया है।

मेरे कार्यक्रम नीचे दिया गया है

import java.util.concurrent.Semaphore; 

public class MultiThreading { 
    public static void main(String[] args) { 
     Semaphore sem = new Semaphore(1,false); 
     for(int i=5;i>=1;i--) 
      new MyThread(i, sem); 
    } 
} 
class MyThread implements Runnable { 
    int var;Semaphore sem; 
    public MyThread(int a, Semaphore s) { 
     var =a;sem=s; 
     new Thread(this).start(); 
    } 
    @Override 
    public void run() { 
     System.out.println("Acquiring lock -- "+var); 
     try { 
      sem.acquire(var); 
     } catch (InterruptedException e) { 
      e.printStackTrace(); 
     } 

     System.out.println(var); 

     System.out.println("Releasing lock -- "+var); 
     sem.release(var+1); 
    } 
} 

आउटपुट है:

हासिल लॉक - 4
हासिल लॉक - 5
हासिल लॉक - 3
हासिल करना ताला - 2
लॉक प्राप्त करना - 1
लॉक जारी करना - 1

जबकि अगर मैं tryAququire के साथ अपना कोड संशोधित करता हूं, तो यह पूरी तरह से अच्छी तरह से चलता है। नीचे नई रन कार्यान्वयन है

@Override 
public void run() { 
    boolean acquired = false; 
    while(!acquired) { 
     acquired = sem.tryAcquire(var); 
    } 
    System.out.println(var); 
    sem.release(var+1); 
} 

किसी सेमाफोर का परमिट प्राप्त तंत्र के बारे में समझाएं कर सकते हैं जब एकाधिक धागे अलग परमिट अनुरोध के साथ इंतजार कर रहे हैं ??

+1

मैं माफी चाहता मैं अपने सवाल का जवाब नहीं कर सकते, लेकिन हूँ। ** ** ** नया थ्रेड (यह) .start(); 'कन्स्ट्रक्टर में करें।जैसा कि आप अभी भी कन्स्ट्रक्टर में हैं, ऑब्जेक्ट पूर्ण नहीं है और आप किसी अन्य विधि में आंशिक रूप से प्रारंभिक ऑब्जेक्ट देते हैं, इस मामले में भी एक और थ्रेड तक। यह वास्तव में वास्तव में बुरा है, ऐसा मत करो। 'रननेबल' लागू करने के बजाय बेहतर 'थ्रेड' बढ़ाएं और फिर 'नया MyThread (i, sem) .start();' या 'नया थ्रेड (नया MyThread (i, sem)) प्रारंभ करें(); '। – Vampire

+0

ओह ठीक है, मैं इसे सत्यापित करूँगा। इस पर ध्यान दिलाने के लिए धन्यवाद। – user1474053

+0

@ BjörnKautler सिर्फ यह कहने के बजाय "* यह वास्तव में बहुत बुरा है, ऐसा मत करो। *" व्याख्या करने की कोशिश करें * क्यों * ताकि लोग अंतर्निहित मुद्दे को समझ सकें। – dimo414

उत्तर

4

यह एक चालाक रणनीति है, लेकिन आप गलतफहमी कर रहे हैं कि कैसे Sempahore हैंड आउट परमिट।आप अपने कोड पर्याप्त बार चलाते हैं तो आप वास्तव में यह दो चरणों वाली तक पहुँचने देखेंगे:

Acquiring lock -- 5 
Acquiring lock -- 1 
1 
Releasing lock -- 1 
Acquiring lock -- 3 
Acquiring lock -- 2 
2 
Acquiring lock -- 4 
Releasing lock -- 2 

आप पर रखने अगर यह पर्याप्त बार फिर से चल रहा है आप वास्तव में देखना चाहते हैं यह सफलतापूर्वक समाप्त। यह Semaphore हाथों परमिट के कारण होता है। आप मान रहे हैं कि Semaphoreacquire() कॉल को समायोजित करने की कोशिश करेगा जैसे ही इसे करने के लिए पर्याप्त परमिट हो। अगर हम Semaphore.aquire(int) के लिए दस्तावेज़ पर ध्यान से देखो हम देखेंगे कि इस मामले (जोर मेरा) नहीं है:

अपर्याप्त परमिट उपलब्ध कर रहे हैं तो वर्तमान धागा धागा शेड्यूलिंग प्रयोजनों के लिए विकलांग हो जाता है और जब तक निष्क्रिय है .. कुछ अन्य धागे इस सेमफोर के लिए release विधियों में से एक को आमंत्रित करते हैं, वर्तमान थ्रेड असाइन किए गए परमिट के बगल में है और उपलब्ध परमिट की संख्या इस अनुरोध को पूरा करती है।

दूसरे शब्दों Semaphore में लंबित अधिग्रहण अनुरोध की एक कतार रहती है और .release() की प्रत्येक कॉल पर, केवल कतार के सिर जाँच करता है। विशेष रूप से यदि आप निष्पक्ष कतार (true पर दूसरा कन्स्ट्रक्टर तर्क सेट करते हैं) तो आप देखेंगे कि कोई चरण भी नहीं होता है, क्योंकि चरण 5 (आमतौर पर) कतार में पहला और यहां तक ​​कि acquire() कॉल भी पूरा हो सकता है अन्य लंबित कॉल के पीछे कतारबद्ध रहें।

संक्षेप में इसका मतलब है कि आप जितनी जल्दी हो सके .acquire() पर भरोसा नहीं कर सकते, जैसा कि आपका कोड मानता है।

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

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


तो इसके बजाय आपको क्या करना चाहिए? java.util.concurrent में कई संभावित रूप से उपयोगी टूल हैं, और जो कुछ भी आप करने की कोशिश कर रहे हैं उस पर निर्भर करता है। यह देखते हुए कि आप प्रत्येक धागे को प्रभावी रूप से अगले धागे से शुरू कर रहे हैं, मैं सिंक्रनाइज़ेशन सहायता के रूप में BlockingQueue का उपयोग कर सकता हूं, जिससे प्रत्येक चरण कतार में अगले चरण को दबाया जा सकता है। प्रत्येक थ्रेड तब कतार को मतदान करेगा, और यदि यह सक्रिय थ्रेड की बारी नहीं है तो मूल्य को प्रतिस्थापित करें और फिर से प्रतीक्षा करें।

यहाँ एक उदाहरण है:

public class MultiThreading { 
    public static void main(String[] args) throws Exception{ 
    // Use fair queuing to prevent an out-of-order task 
    // from jumping to the head of the line again 
    // try setting this to false - you'll see far more re-queuing calls 
    BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(1, true); 
    for (int i = 5; i >= 1; i--) { 
     Thread.sleep(100); // not necessary, just helps demonstrate the queuing behavior 
     new MyThread(i, queue).start(); 
    } 
    queue.add(1); // work starts now 
    } 

    static class MyThread extends Thread { 
    int var; 
    BlockingQueue<Integer> queue; 

    public MyThread(int var, BlockingQueue<Integer> queue) { 
     this.var = var; 
     this.queue = queue; 
    } 

    @Override 
    public void run() { 
     System.out.println("Task " + var + " is now pending..."); 
     try { 
     while (true) { 
      int task = queue.take(); 
      if (task != var) { 
      System.out.println(
       "Task " + var + " got task " + task + " instead - re-queuing"); 
      queue.add(task); 
      } else { 
      break; 
      } 
     } 
     } catch (InterruptedException e) { 
     // If a thread is interrupted, re-mark the thread interrupted and terminate 
     Thread.currentThread().interrupt(); 
     return; 
     } 

     System.out.println("Finished task " + var); 

     System.out.println("Registering task " + (var + 1) + " to run next"); 
     queue.add(var + 1); 
    } 
    } 
} 

यह निम्न प्रिंट और सफलतापूर्वक समाप्त हो जाता है:

Task 5 is now pending... 
Task 4 is now pending... 
Task 3 is now pending... 
Task 2 is now pending... 
Task 1 is now pending... 
Task 5 got task 1 instead - re-queuing 
Task 4 got task 1 instead - re-queuing 
Task 3 got task 1 instead - re-queuing 
Task 2 got task 1 instead - re-queuing 
Finished task 1 
Registering task 2 to run next 
Task 5 got task 2 instead - re-queuing 
Task 4 got task 2 instead - re-queuing 
Task 3 got task 2 instead - re-queuing 
Finished task 2 
Registering task 3 to run next 
Task 5 got task 3 instead - re-queuing 
Task 4 got task 3 instead - re-queuing 
Finished task 3 
Registering task 4 to run next 
Task 5 got task 4 instead - re-queuing 
Finished task 4 
Registering task 5 to run next 
Finished task 5 
Registering task 6 to run next 
+0

शानदार दृष्टिकोण। बहुत बहुत धन्यवाद, मैं इसे अब समझता हूं। हम ब्लॉकिंगक्यूयू के बजाय परमाणु चर का भी उपयोग कर सकते हैं या मतदान तंत्र से बचने के लिए कंडीशन का उपयोग कर सकते हैं। – user1474053

+0

@ user1474053 मदद करने में खुशी हुई। आप वास्तव में क्या करने की कोशिश कर रहे हैं, इस पर निर्भर करते हुए निश्चित रूप से कई अलग-अलग उचित समाधान हैं, लेकिन यदि आप एक निर्धारित आदेश में अलग-अलग धागे कार्य सुनिश्चित करना चाहते हैं तो आपको कुछ प्रकार के अवरोधन तंत्र का उपयोग करना चाहिए; एक परमाणु चर को नियमित रूप से सभी धागे द्वारा मतदान किया जाना चाहिए। – dimo414

2

Semaphore.acquire(int) के लिए जावाडोक का कहना है:

If insufficient permits are available then the current thread becomes 
disabled for thread scheduling purposes and lies dormant until one of 
two things happens: 

Some other thread invokes one of the release methods for this semaphore, 
the current thread is next to be assigned permits and the number of 
available permits satisfies this request [or the thread is interrupted]. 

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

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

+1

अच्छा स्पष्टीकरण। आपके लिए धन्यवाद मुझे समझने में चूक गया "वर्तमान धागा आवंटित परमिट के बगल में है" लाइन। – user1474053