2009-06-10 14 views
30

मैं अपने निर्माता (1/100 एमएस मामलों के लिए) के लिए महत्वपूर्ण समय आवश्यकताओं के साथ एक मानक जावा सिस्टम पर काम कर रहा हूं।कौन सा जावा अवरोधक कतार एकल-निर्माता सिंगल-उपभोक्ता परिदृश्यों के लिए सबसे अधिक कुशल है

मेरे पास एक अवरोधक कतार में सामान रखने वाला निर्माता है, और एक उपभोक्ता बाद में उस सामान को उठा रहा है और उसे फ़ाइल में डंप कर रहा है। डेटा उपलब्ध नहीं होने पर उपभोक्ता ब्लॉक।

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

क्या कोई कार्यान्वयन है जो तेजी से हो सकता है क्योंकि मेरे पास केवल एक उपभोक्ता और एकल निर्माता है?

+1

निम्नलिखित आप रीयल-टाइम JVM खरीदने पर विचार किया था देखते हैं? इस तरह के महत्वपूर्ण समय फ्रेम प्राप्त करने में मदद करने के लिए विशेष एक्सटेंशन हैं। http://java.sun.com/javase/technologies/realtime/index.jsp –

+1

@Rastislv: मैं मानता हूं कि आरटी जेवीएम बेहतर होगा। हालांकि, यह एक मौजूदा प्रणाली है और अधिकांश ग्राहक मानक जेवीएम का उपयोग करते हैं; मुझे न्यूनतम प्रभाव के साथ बेंचमार्किंग करने की ज़रूरत है, और मैं उन वर्गों को माप रहा हूं जो 1/100 एमएस – Uri

+0

लेते हैं [पारंपरिक रूप से [एलएमएक्स विघटनकर्ता] (https://lmax-exchange.github.io/disruptor/) का उपयोग पारंपरिक कतार के बजाय करें। –

उत्तर

31

ठीक है, वास्तव में बहुत सारे विकल्प नहीं हैं। मुझे listed subclasses के माध्यम से चलते हैं:

DelayQueue, LinkedBlockingDeque, PriorityBlockingQueue, और SynchronousQueue सभी अतिरिक्त कार्यक्षमता की आवश्यकता होती है विशेष मामलों के लिए बने हैं; वे इस परिदृश्य में समझ में नहीं आता है।

जो केवल ArrayBlockingQueue और LinkedBlockingQueue छोड़ देता है। यदि आप जानते हैं कि आपको ArrayList या LinkedList की आवश्यकता है या नहीं, तो आप शायद इसे स्वयं उत्तर दे सकते हैं।

ध्यान दें कि LinkedBlockingQueue में, "प्रत्येक प्रविष्टि पर जुड़े नोड्स गतिशील रूप से बनाए जाते हैं"; यह आपको ArrayBlockingQueue की तरफ धक्का दे सकता है।

+0

@mmyers: Wouln't ArrayBlockingQueue पूरे सरणी को लॉक करते हैं जबकि LinkedBlockingQueue सिर और पूंछ को लॉक करने के लिए व्यवस्थित हो सकता है? – Uri

+0

यह एक अच्छा मुद्दा है। वे दोनों ReentrantLocks का उपयोग करते हैं, लेकिन ArrayBlockingQueue केवल एक है जबकि LinkedBlockingQueue प्रत्येक छोर के लिए एक है। –

+9

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

3

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

यदि मुझे अनुमान लगाया गया था, तो मैं ArrayBlockingQueue के साथ जाऊंगा क्योंकि सरणी-आधारित संग्रह में संदर्भ के अच्छे इलाके होते हैं।

5

LinkedBlockingQueue में ओ (1) प्रविष्टि लागत होगी जब तक स्मृति आवंटन विलंब न हो। एक बहुत बड़ा ArrayBlockingQueue में ओ (1) सम्मिलन लागत होगी जब तक कि कचरा संग्रह के कारण सिस्टम अवरुद्ध न हो; हालांकि, क्षमता पर जब डालने पर ब्लॉक होगा।

समवर्ती कचरा संग्रह के साथ भी मुझे यकीन नहीं है कि आपको एक प्रबंधित भाषा में रीयल-टाइम सिस्टम लिखना चाहिए या नहीं।

+0

क्या लिंक्डब्लॉकिंग क्यूयू पूल यह नोड्स है, या क्या मैं हर बार लागत का भुगतान करूँगा? एक सरणी के साथ मैं कम से कम अंतरिक्ष आवंटित कर सकता हूं ... – Uri

+0

प्रलेखन कहता है कि नोड्स गतिशील रूप से आवंटित किए जाते हैं: "लिंक किए गए नोड्स प्रत्येक प्रविष्टि पर गतिशील रूप से बनाए जाते हैं जब तक यह क्षमता से ऊपर कतार नहीं लाएगा" –

+0

यहां लिंकडब्लॉकिंगक्यूयू का स्रोत है। http://fuseyism.com/classpath/doc/java/util/concurrent/LinkedBlockingQueue-source.html यह किसी भी तरह की प्रीलोक्शन नहीं कर रहा है। –

2

ArrayBlockingQueue शायद तेज़ होगा क्योंकि आपको सम्मिलन पर लिंक नोड्स बनाने की आवश्यकता नहीं है। नोट, हालांकि, ArrayBlockingQueue एक निश्चित लंबाई का है, यदि आप अपनी कतार को मनमाने ढंग से बड़े (कम से कम Integer.MAX_INT) बढ़ाना चाहते हैं तो आपको एक LinkedBlockingQueue का उपयोग करना होगा (जब तक कि आप Integer.MAX_INT तत्वों की एक सरणी आवंटित नहीं करना चाहते) ।

3

मैं एंड्रयू डफी की टिप्पणी से सहमत हूं कि जावा में ऐसी समय-बाधित प्रणाली को लागू करना एक गलती हो सकती है। भले ही, आप मानते हैं कि आप JVM से विवाहित हैं, और आप इस कतार को पूर्ण होने की उम्मीद नहीं करते हैं (यानी उपभोक्ता लोड को संभाल सकता है), मुझे लगता है कि आपको कस्टम कार्यान्वयन द्वारा सर्वोत्तम सेवा दी जा सकती है, जैसे कि ऐरेब्लॉकिंगक्यूयू लेकिन एकल निर्माता/उपभोक्ता परिदृश्य के लिए अनुकूलित/अनुकूलित किया गया। विशेष रूप से, मुझे अवरोध करने के बजाए अंतरिक्ष की प्रतीक्षा करने के लिए निर्माता-पक्ष पर कताई की धारणा पसंद है।

मैंने जावा को संदर्भित किया।मार्गदर्शन के लिए समवर्ती sources, और इस एल्गोरिदम का मसौदा तैयार किया गया ...

यह मेरे लिए बहुत अच्छा लग रहा है, लेकिन यह पूरी तरह से अनचाहे है और शायद अभ्यास में कोई तेज़ नहीं हो सकता है (शायद क्रांतिकारी नहीं, लेकिन मैंने इसे स्वयं क्रैंक किया: -)। मैं इसके साथ मजाक कर रहा था, वैसे भी ... क्या आपको कोई दोष मिल सकता है?

स्यूडोकोड:

private final ReentrantLock takeLock = new ReentrantLock(); 
private final Condition notEmpty = takeLock.newCondition(); 
private final E[] buffer; 
private int head = 0 
private int tail = 0; 

public void put(E e) { 
    if (e == null) throw NullPointerException(); 

    while (buffer[tail] != null) { 
    // spin, wait for space... hurry up, consumer! 
    // open Q: would a tight/empty loop be superior here? 
    Thread.sleep(1); 
    } 

    buffer[tail] = e; 
    tail = (tail + 1) % buffer.length; 

    if (count.getAndIncrement() == 0) { 
    sync(takeLock) { // this is pseudocode -- should be lock/try/finally/unlock 
     notEmpty.signal(); 
    } 
    } 
} 

public E take() { 
    sync(takeLock) { 
    while (count.get() == 0) notEmpty.await(); 
    } 
    E item = buffer[head]; 
    buffer[head] = null; 
    count.decrement(); 
    head = (head + 1) % buffer.length; 

    return item; 
} 
12

आप जावा में एक विलंबता संवेदनशील प्रणाली में लिख सकते हैं, लेकिन आप स्मृति आवंटन, जानकारी धागे और लॉकिंग के बीच से गुजरने की सावधान रहना होगा। आपको यह देखने के लिए कुछ सरल परीक्षण लिखना चाहिए कि इन चीजों को आपके सर्वर पर कितना समय लगता है। (वे ओएस और मेमोरी आर्किटेक्चर पर आधार बदलते हैं)

यदि आप ऐसा करते हैं तो आप 99.99% तक संचालन के लिए स्थिर समय प्राप्त कर सकते हैं। यह उचित वास्तविक समय नहीं है, लेकिन यह काफी करीब हो सकता है। एक व्यापारिक प्रणाली पर, सी लागत के बजाय जावा का उपयोग करके £ 100/डी खर्च हो सकता है, हालांकि जावा की बजाय सी/सी ++ में विकास की लागत इस से काफी अधिक होने की संभावना है। जैसे लचीलापन के मामले में यह हमें और बगों की संख्या देता है जो इसे बचाता है।

आप एक ही चीज करने वाले सी प्रोग्राम में देख सकने वाले एक ही मात्रा में जिटर को काफी करीब से प्राप्त कर सकते हैं। कुछ इस "सी-जैसे" जावा कहते हैं।

दुर्भाग्य से जावा में धागे के बीच ऑब्जेक्ट को पास करने के लिए लगभग 8 माइक्रो-सेकेंड लगता है जो सर्वर पर एक ऐरेब्लॉकिंगक्यूयू के माध्यम से काम करता है और मेरा सुझाव है कि आप इसे अपने सर्वर पर जांचें। एक सरल परीक्षण सिस्टम.नानोटाइम() को थ्रेड के बीच पास करना है और यह देखना कितना समय लगता है।

ऐरेब्लॉकिंगक्यूयू में एक "फीचर" है जहां यह प्रत्येक तत्व को जोड़ा गया ऑब्जेक्ट बनाता है (हालांकि ऐसा करने का कोई अच्छा कारण नहीं है) इसलिए यदि आपको मानक कार्यान्वयन मिलता है जो ऐसा नहीं करता है, तो मुझे बताएं ।

+0

आईएमई यह निर्धारित करने लायक है कि टाइमर अंतर्निहित नैनोटाइम का संकल्प आपके हार्डवेयर पर है यदि आप इस तरह के समय पर हैं, तो कुछ ओएस/सीपीयू संयोजनों पर चीजें बहुत ही कमजोर हो सकती हैं। – Matt

2

आप LinkedTransferQueue का उपयोग कर देख सकते हैं, जो "स्लेक के साथ दोहरी कतार" लागू करता है और उत्पादकों और उपभोक्ताओं से मेल खाने में अच्छा है। अधिक जानकारी के Java 7 TransferQueue, Dual Synchronous Queues (.pdf) और LinkedTransferQueue source

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