2017-07-11 15 views
8

मान लीजिए मैं इस कोड है:समानांतर flatMap हमेशा अनुक्रमिक

Collections.singletonList(10) 
      .parallelStream() // .stream() - nothing changes 
      .flatMap(x -> Stream.iterate(0, i -> i + 1) 
        .limit(x) 
        .parallel() 
        .peek(m -> { 
         System.out.println(Thread.currentThread().getName()); 
        })) 
      .collect(Collectors.toSet()); 

आउटपुट एक ही धागे नाम है, इसलिए वहाँ parallel यहां से कोई लाभ नहीं है - क्या मैं उस से मतलब किसी एकल थ्रेड कि करता है कि वहाँ है पूरा काम।

flatMap अंदर इस कोड है:

result.sequential().forEach(downstream); 

मैं sequential संपत्ति के लिए मजबूर कर समझते हैं "बाहरी" धारा समानांतर होगा (वे सकता है शायद ब्लॉक), "बाहरी" "flatMap के लिए इंतजार करना होगा "खत्म करने के लिए और दूसरी तरफ (जैसा कि एक ही आम पूल का उपयोग किया जाता है) लेकिन क्यों हमेशा बल है कि?

क्या उन चीजों में से एक है जो बाद के संस्करण में बदल सकता है?

उत्तर

8

दो अलग-अलग पहलू हैं।

सबसे पहले, केवल एक ही पाइपलाइन है जो अनुक्रमिक या समांतर है। आंतरिक धारा में अनुक्रमिक या समांतर की पसंद अप्रासंगिक है। ध्यान दें कि downstream जो कोड आप उद्धृत कोड स्निपेट में देखते हैं, वह पूरे बाद की स्ट्रीम पाइपलाइन का प्रतिनिधित्व करता है, इसलिए आपके कोड में, .collect(Collectors.toSet()); के साथ समाप्त होने पर, यह उपभोक्ता अंततः परिणामी तत्वों को एक Set उदाहरण में जोड़ देगा जो थ्रेड सुरक्षित नहीं है। तो उस एकल उपभोक्ता के साथ समानांतर में भीतरी धारा को संसाधित करना पूरे ऑपरेशन को तोड़ देगा।

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

जिस तरह से इसे लागू किया गया है, Why filter() after flatMap() is “not completely” lazy in Java streams? समस्या का कारण भी है, क्योंकि forEach को आंतरिक धारा पर बुलाया जाता है जो सभी तत्वों को डाउनस्ट्रीम उपभोक्ता को पास कर देगा। जैसा कि this answer द्वारा दिखाया गया है, एक वैकल्पिक कार्यान्वयन, आलस्य और पदार्थों के विभाजन का समर्थन करना संभव है। लेकिन यह इसे लागू करने का मूल रूप से अलग तरीका है। स्ट्रीम कार्यान्वयन का वर्तमान डिज़ाइन ज्यादातर उपभोक्ता संरचना द्वारा काम करता है, इसलिए अंत में, स्रोत स्प्लिटरेटर (और इससे अलग होकर) Consumer को tryAdvance या forEachRemaining में संपूर्ण स्ट्रीम पाइपलाइन का प्रतिनिधित्व करता है। इसके विपरीत, लिंक्ड उत्तर का समाधान स्प्लिटरेटर संरचना करता है, जो स्रोत स्प्लिटरेटर्स को एक नया Spliterator प्रस्तुत करता है। मुझे लगता है कि दोनों दृष्टिकोणों के फायदे हैं और मुझे यकीन नहीं है कि ओपनजेडीके कार्यान्वयन दूसरे तरीके से काम करते समय कितना खो जाएगा।

+0

हाय, महोदय। क्या यह एक स्ट्रीम बग होना चाहिए? –

+1

@ होली-जावा मैं यह नहीं कहूंगा कि यह एक बग है, केवल खराब कार्यान्वयन डिज़ाइन जो भविष्य में सबसे अधिक तय किया जाएगा। –

+5

@ होली-जावा: गायब आलस्य को एक बग के रूप में देखा जा सकता है और इसके लिए पहले से ही एक बग रिपोर्ट है। सीमित समांतरता, हालांकि, संभावित प्रदर्शन सुधार के लिए सिर्फ एक क्षेत्र है। व्यवहार में, यह केवल बाहरी धाराओं और बहुत बड़ी आंतरिक धाराओं में तत्वों की एक छोटी संख्या के साथ धाराओं को प्रभावित करता है। – Holger

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