2017-02-27 11 views
5

मेरे पास 3 CompletableFutures सभी 3 अलग-अलग डेटा प्रकार लौट रहे हैं।जावा 8 कॉम्प्लेबल फ्यूचर्स सभी अलग-अलग डेटा प्रकारों के

मैं परिणाम ऑब्जेक्ट बनाना चाहता हूं जो कि सभी 3 वायदा द्वारा किए गए परिणाम की संरचना है।

तो मेरी वर्तमान कार्यशील कोड इस तरह दिखता है:

public ClassD getResultClassD() { 

    ClassD resultClass = new ClassD(); 
    CompletableFuture<ClassA> classAFuture = CompletableFuture.supplyAsync(() -> service.getClassA()); 
    CompletableFuture<ClassB> classBFuture = CompletableFuture.supplyAsync(() -> service.getClassB()); 
    CompletableFuture<ClassC> classCFuture = CompletableFuture.supplyAsync(() -> service.getClassC()); 

    CompletableFuture.allOf(classAFuture, classBFuture, classCFuture) 
        .thenAcceptAsync(it -> { 
         ClassA classA = classAFuture.join(); 
         if (classA != null) { 
          resultClass.setClassA(classA); 
         } 

         ClassB classB = classBFuture.join(); 
         if (classB != null) { 
          resultClass.setClassB(classB); 
         } 

         ClassC classC = classCFuture.join(); 
         if (classC != null) { 
          resultClass.setClassC(classC); 
         } 

        }); 

    return resultClass; 
} 

मेरे प्रश्न हैं:

  1. यहाँ मेरी धारणा है कि के बाद से मैं allOf और thenAcceptAsync का उपयोग कर रहा हूँ कि इस कॉल गैर अवरुद्ध हो जाएगा। क्या मेरी समझ सही है?

  2. क्या यह विभिन्न परिणाम प्रकारों को वापस करने वाले कई वायदा से निपटने का सही तरीका है?

  3. thenAcceptAsync के भीतर ClassD ऑब्जेक्ट का निर्माण करने का अधिकार है?

  4. क्या यह join या getNow विधि का उपयोग करने के लिए उपयुक्त है AcceptAsync lambda में?

उत्तर

6

आपका प्रयास सही दिशा में जा रहा है, लेकिन सही नहीं है। आपकी विधि getResultClassD()ClassD प्रकार की पहले से ही तत्काल ऑब्जेक्ट लौटाती है जिस पर एक मनमाने ढंग से थ्रेड getResultClassD() नोटर के कॉलर के बिना संशोधित विधियों को कॉल करेगा। यह रेस की स्थिति पैदा कर सकता है, अगर संशोधित विधियां अपने आप थ्रेड सुरक्षित नहीं हैं, तो कॉलर कभी नहीं जान पाएगा, जब ClassD उदाहरण वास्तव में उपयोग के लिए तैयार है।

एक सही समाधान होगा:

public CompletableFuture<ClassD> getResultClassD() { 

    CompletableFuture<ClassA> classAFuture 
     = CompletableFuture.supplyAsync(() -> service.getClassA()); 
    CompletableFuture<ClassB> classBFuture 
     = CompletableFuture.supplyAsync(() -> service.getClassB()); 
    CompletableFuture<ClassC> classCFuture 
     = CompletableFuture.supplyAsync(() -> service.getClassC()); 

    return CompletableFuture.allOf(classAFuture, classBFuture, classCFuture) 
     .thenApplyAsync(dummy -> { 
      ClassD resultClass = new ClassD(); 

      ClassA classA = classAFuture.join(); 
      if (classA != null) { 
       resultClass.setClassA(classA); 
      } 

      ClassB classB = classBFuture.join(); 
      if (classB != null) { 
       resultClass.setClassB(classB); 
      } 

      ClassC classC = classCFuture.join(); 
      if (classC != null) { 
       resultClass.setClassC(classC); 
      } 

      return resultClass; 
     }); 
} 

अब, getResultClassD() के फोन करने वाले लौटे CompletableFuture उपयोग कर सकते हैं एक बार आपरेशन पूरा हो गया है परिणाम पुनः प्राप्त करने, प्रगति राज्य या श्रृंखला निर्भर कार्यों क्वेरी या join() उपयोग करने के लिए ।

अन्य प्रश्नों को हल करने के लिए, हाँ, यह ऑपरेशन असीमित है और लैम्ब्डा अभिव्यक्तियों के भीतर join() का उपयोग उचित है। join बिल्कुल बनाया गया था क्योंकि Future.get(), जिसे चेक अपवादों को फेंकने के लिए घोषित किया गया है, इन लैम्ब्डा अभिव्यक्तियों के भीतर उपयोग को अनावश्यक रूप से कठिन बनाता है।

ध्यान दें कि null परीक्षण केवल उपयोगी हैं, यदि इन service.getClassX() वास्तव में null वापस कर सकते हैं। यदि सेवा कॉल में से एक अपवाद के साथ विफल रहता है, तो संपूर्ण ऑपरेशन (CompletableFuture<ClassD> द्वारा प्रतिनिधित्व) असाधारण रूप से पूरा हो जाएगा।

+0

विस्तृत उत्तर के लिए धन्यवाद। आपके उत्तर का मेरा एकमात्र अनुवर्ती यह है कि तब एपलीएसिंक के पास रिटर्नटेबल फ्यूचर का रिटर्न प्रकार है, यह कैसे काम करेगा और इस विधि को कैसे लागू करेगा और परिणाम –

+4

नहीं होगा, नहीं, यह 'allOf' का रिटर्न प्रकार है जो 'CompletableFuture ' , यही कारण है कि 'passApplyAsync' को पारित किया गया कार्य इनपुट के रूप में 'शून्य' प्राप्त करता है ('डमी'> के बजाय उपरोक्त 'डमी' पैरामीटर, आप '(शून्य डमी) -> 'भी लिख सकते हैं। फिर, फ़ंक्शन 'शून्य' इनपुट (वास्तव में इसे अनदेखा करके) को 'क्लासडी' परिणाम में अनुवादित करता है, इसलिए 'thenApplyAsync' का परिणाम' CompletableFuture 'होगा। – Holger

+1

@ होल्गर मैं आपके लिए एक समान मार्ग पर जा रहा था लेकिन मैं वैकल्पिक कॉल का उपयोग कर रहा था। सेवा कॉल में अनुपयोगी, इसलिए आप 'cCFuture.join()। IfPresent (कक्षा :: SetStuff)' – Ash

3

मैं क्या @Holger अपने जवाब में क्या कर रहा था, लेकिन एक वैकल्पिक में सेवा कॉल लपेटकर लिए एक समान मार्ग है, जो सुराग thenApplyAsync चरण

CompletableFuture<Optional<ClassA>> classAFuture 
    = CompletableFuture.supplyAsync(() -> Optional.ofNullable(service.getClassA()))); 

CompletableFuture<Optional<ClassB>> classBFuture 
    = CompletableFuture.supplyAsync(() -> Optional.ofNullable(service.getClassB())); 

CompletableFuture<Optional<ClassC>> classCFuture 
    = CompletableFuture.supplyAsync(() -> Optional.ofNullable(service.getClassC())); 

return CompletableFuture.allOf(classAFuture, classBFuture, classCFuture) 
    .thenApplyAsync(dummy -> { 
     ClassD resultClass = new ClassD(); 

     classAFuture.join().ifPresent(resultClass::setClassA) 
     classBFuture.join().ifPresent(resultClass::setClassB) 
     classCFuture.join().ifPresent(resultClass::setClassC) 

     return resultClass; 
    }); 
+1

हाँ विकल्प का उपयोग करना –

0

में एक और तरीका है संभाल करने के लिए नीचे जा रहा था क्लीनर कोड के लिए यदि आप घोषित नहीं करना चाहते हैं तो कई चर का उपयोग करने के लिए कॉम्बाइन या फिर कॉम्बाइनएसिंक का उपयोग अपने वायदा को एक साथ करने के लिए करना है।

public CompletableFuture<ClassD> getResultClassD() 
{ 
    return CompletableFuture.supplyAsync(ClassD::new) 
    .thenCombine(CompletableFuture.supplyAsync(service::getClassA), (d, a) -> { 
     d.setClassA(a); 
     return d; 
    }) 
    .thenCombine(CompletableFuture.supplyAsync(service::getClassB), (d, b) -> { 
     d.setClassB(b); 
     return d; 
    }) 
    .thenCombine(CompletableFuture.supplyAsync(service::getClassC), (d, c) -> { 
     d.setClassC(c); 
     return d; 
    }); 
} 

गेटर्स को अभी भी असीमित रूप से निकाल दिया जाएगा और परिणामों को क्रम में निष्पादित किया जाएगा। यह एक ही परिणाम प्राप्त करने के लिए मूल रूप से एक और वाक्यविन्यास विकल्प है।

+0

के बारे में सोचने का सही तरीका है यदि संभवतः 2 या 3 वायदा गठबंधन करने के लिए यह एक अच्छा तरीका है। हालांकि आपको शायद 'CompletableFuture.completedFuture (new classD()) से शुरू करना चाहिए क्योंकि तत्कालता असीमित रूप से चलने योग्य नहीं है। वास्तव में, आप इसे पहले भविष्य में 'thenApply()' में भी तुरंत चालू कर सकते हैं। –