2011-04-09 5 views
7

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

उदाहरण के लिए:

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

सैमफोरों के साथ संयम बनाए रखने के दौरान मैं उस डेटा को आगे और आगे कैसे पास करूं?

इसके अलावा, एकमात्र वर्ग जिसे मैं java.util.concurrent से उपयोग कर सकता हूं Semaphore कक्षा है।

+1

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

उत्तर

7

थ्रेड के बीच डेटा को आगे और आगे पास करने का एक आसान तरीका पैकेज java.util.concurrent में स्थित इंटरफ़ेस BlockingQueue<E> के कार्यान्वयन का उपयोग करना है।

  • add(E):

    इस इंटरफेस अलग व्यवहार के साथ संग्रह करने के लिए तत्वों को जोड़ने के तरीकों है यदि संभव हो तो कहते हैं, नहीं तो अपवाद

  • boolean offer(E) फेंकता है: TRUE देता तत्व जोड़ दिया गया है, अन्यथा गलत
  • boolean offer(E, long, TimeUnit): तत्व जोड़ने की कोशिश करता है, निर्दिष्ट समय की प्रतीक्षा
  • put(E): जब तक तत्व जोड़ा नहीं जाता है तब तक कॉलिंग थ्रेड को अवरुद्ध करता है

    • take():

    यह भी समान व्यवहार के साथ तत्व पुनः प्राप्ति के लिए तरीकों को परिभाषित करता है ब्लॉक एक तत्व उपलब्ध

  • poll(long, TimeUnit) वहाँ तक: एक तत्व को पुन: प्राप्त या रिटर्न शून्य

कार्यान्वयन मैं अक्सर उपयोग करता हूं: ArrayBlockingQueue, LinkedBlockingQueue और SynchronousQueue

पहला, ArrayBlockingQueue, का एक निश्चित आकार है, जो उसके कन्स्ट्रक्टर को पास किए गए पैरामीटर द्वारा परिभाषित किया गया है।

दूसरा, LinkedBlockingQueue, आकार का आकार है। यह हमेशा किसी भी तत्व को स्वीकार करेगा, यानी offer तुरंत वापस आ जाएगा, add कभी अपवाद नहीं फेंक देगा।

तीसरा, और मेरे लिए सबसे दिलचस्प एक, SynchronousQueue, बिल्कुल एक पाइप है। आप इसे आकार 0 के साथ कतार के रूप में सोच सकते हैं। यह कभी भी तत्व नहीं रखेगा: यह कतार केवल तत्वों को स्वीकार करेगी यदि कुछ अन्य थ्रेड इसके तत्वों को पुनर्प्राप्त करने का प्रयास कर रहे हैं। इसके विपरीत, एक पुनर्प्राप्ति ऑपरेशन केवल एक तत्व लौटाएगा यदि कोई अन्य थ्रेड इसे धक्का देने का प्रयास कर रहा है।

होमवर्क संकेतबाहु के साथ विशेष रूप से किया तुल्यकालन की आवश्यकता को पूरा करने के लिए आपको वर्णन मैं SynchronousQueue के बारे में आप दे दी द्वारा प्रेरित हो सकता है, और काफी कुछ इसी तरह लिखें:

class Pipe<E> { 
    private E e; 

    private final Semaphore read = new Semaphore(0); 
    private final Semaphore write = new Semaphore(1); 

    public final void put(final E e) { 
    write.acquire(); 
    this.e = e; 
    read.release(); 
    } 

    public final E take() { 
    read.acquire(); 
    E e = this.e; 
    write.release(); 
    return e; 
    } 
} 

सूचना यह है कि कक्षा सिंक्रोनस्यूयूयू के बारे में वर्णित किए गए समान व्यवहार को प्रस्तुत करती है।

एक बार विधियों put(E) को कहा जाता है कि यह लिखने सेमफोर प्राप्त करता है, जिसे खाली छोड़ दिया जाएगा, ताकि एक ही विधि के लिए एक और कॉल अपनी पहली पंक्ति पर अवरुद्ध हो। यह विधि तब पारित होने वाली वस्तु का संदर्भ संग्रहीत करती है, और पढ़ने सेमफोर जारी करती है। इस रिलीज से आगे बढ़ने के लिए take() विधि को कॉल करने वाले किसी भी थ्रेड के लिए यह संभव हो जाएगा।

take() विधि का पहला चरण तब, स्वाभाविक रूप से, पढ़ा सैमफोर प्राप्त करने के लिए, तत्व को पुनः प्राप्त करने के लिए किसी अन्य थ्रेड को अस्वीकार करने के लिए किया जाता है। तत्व को पुनर्प्राप्त करने के बाद और स्थानीय चर में रखा गया है (व्यायाम: क्या होगा यदि उस पंक्ति, ई ई = this.e को हटा दिया गया था?), विधि लिखने सेमफोर जारी करती है, ताकि विधि put(E) हो किसी भी थ्रेड द्वारा फिर से बुलाया जाता है, और स्थानीय चर में जो बचाया गया है उसे लौटाता है।

एक महत्वपूर्ण टिप्पणी के रूप में, देख सकते हैं कि वस्तु पारित किया जा रहा करने के लिए संदर्भ एक निजी क्षेत्र में रखा जाता है, और तरीकों take() और put(E) दोनों अंतिम हैं। यह अत्यंत महत्वपूर्ण है, और अक्सर याद किया जाता है। यदि ये विधियां अंतिम नहीं थीं (या बदतर, फ़ील्ड निजी नहीं है), एक विरासत वर्ग take() और put(E) अनुबंध को तोड़ने के व्यवहार को बदलने में सक्षम होगा। अगर सिर्फ try/finally के उपयोग को दिखाने के लिए चला जाता है कि

class Pipe<E> { 
    // ... 
    public final E take() { 
    try { 
     read.acquire(); 
     return e; 
    } finally { 
     write.release(); 
    } 
    } 
} 

यहाँ, इस उदाहरण के बिंदु:

अंत में, आप इस प्रकार try {} finally {} का उपयोग करके take() विधि में एक स्थानीय चर घोषित करने के लिए आवश्यकता से बचने के सकता है अनुभवहीन डेवलपर्स के बीच अनजान। जाहिर है, इस मामले में, कोई वास्तविक लाभ नहीं है।

ओह अरे, मैंने आपके लिए अपना होमवर्क पूरा कर लिया है। प्रतिशोध में - और आपके लिए सेमफोरस के बारे में अपने ज्ञान का परीक्षण करने के लिए -, आप BlockingQueue अनुबंध द्वारा परिभाषित कुछ अन्य विधियों को क्यों लागू नहीं करते हैं? उदाहरण के लिए, आप offer(E) विधि और take(E, long, TimeUnit) लागू कर सकते हैं!

शुभकामनाएं।

+0

यह होमवर्क आवश्यकता को पूरा नहीं करेगा कि "समरूपता पूरी तरह से semaphores द्वारा किया जाना चाहिए।" (निश्चित रूप से उच्च स्तर की समवर्ती उपयोगिताओं में से एक का उपयोग करके वास्तविक जीवन में सबसे अच्छा विकल्प है।) –

+0

दरअसल! मुझे पहले याद आया। –

+0

हां, एकमात्र आइटम जो मैं java.util.concurrent से उपयोग कर सकता हूं semaphore है। कोई अन्य थ्रेड-सुरक्षित कक्षाओं का उपयोग नहीं किया जा सकता है ... तो इसे पाइप के साथ किया जाना चाहिए, और यदि ऐसा है तो कैसे? – JustinY17

1

इसे पढ़ने/लिखने के लॉक के साथ साझा स्मृति के संदर्भ में सोचें।

  1. संदेश डालने के लिए एक बफर बनाएं।
  2. बफर तक पहुंच को लॉक/सेमफोर का उपयोग करके नियंत्रित किया जाना चाहिए।
  3. अंतर थ्रेड संचार उद्देश्य के लिए इस बफर का उपयोग करें।

सादर

PKV