भविष्य की सूची पर प्रतीक्षा कर रहा है


145

मेरे पास एक ऐसा तरीका है जो Listवायदा का रिटर्न देता है

List<Future<O>> futures = getFutures();

अब मैं तब तक इंतजार करना चाहता हूं जब तक कि सभी वायदा सफलतापूर्वक प्रसंस्करण या किसी भी ऐसे कार्य के लिए नहीं किया जाता है जिसका उत्पादन भविष्य में वापस आ जाता है। यहां तक ​​कि अगर एक कार्य एक अपवाद फेंकता है, तो अन्य वायदा के लिए इंतजार करने का कोई मतलब नहीं है।

के लिए सरल दृष्टिकोण होगा

wait() {

   For(Future f : futures) {
     try {
       f.get();
     } catch(Exception e) {
       //TODO catch specific exception
       // this future threw exception , means somone could not do its task
       return;
     }
   }
}

लेकिन यहां समस्या यह है कि, उदाहरण के लिए, 4 वें भविष्य एक अपवाद फेंकता है, तो मैं पहले 3 वायदा उपलब्ध होने के लिए अनावश्यक रूप से इंतजार करूंगा।

इसे कैसे हल करें? किसी भी तरह से कुंडी मदद की गिनती करेगा? मैं भविष्य का उपयोग करने में असमर्थ हूं isDoneक्योंकि जावा डॉक्टर कहते हैं

boolean isDone()
Returns true if this task completed. Completion may be due to normal termination, an exception, or cancellation -- in all of these cases, this method will return true.

1
कौन उन वायदा करता है? वे किस प्रकार के हैं? इंटरफ़ेस java.util.concurrent.Future आपको इच्छित कार्यक्षमता प्रदान नहीं करता है, एकमात्र तरीका कॉलबैक के साथ अपने फ्यूचर्स का उपयोग करना है।
अलेक्सई कागोरोडोव

आप ExecutionServiceप्रत्येक "बैच" कार्यों के लिए एक उदाहरण बना सकते हैं , उन्हें इसमें जमा कर सकते हैं, फिर तुरंत सेवा बंद कर सकते हैं और awaitTermination()उस पर उपयोग कर सकते हैं जो मुझे लगता है।
मिलीमो।

आप उपयोग कर सकते हैं CountDownLatchयदि आप अपने सभी वायदा के शरीर को लपेटते हैं try..finallyताकि यह सुनिश्चित किया जा सके कि कुंडी खराब हो गई है।
मिलीमो।

docs.oracle.com/javase/7/docs/api/java/util/concurrent/… ठीक वही काम करता है जिसकी आपको जरूरत है।
assylias

@AlexeiKaigorodov हाँ, मेरा भविष्य java.util.concurrent.I प्रकार का है। callable.I के साथ भविष्य में मुकदमा कर रहा हूँ, जब मुझे फ्यूचर के लिए एक कार्य निष्पादित करने के लिए फ्यूचर मिलता है
user93796

जवाबों:


124

आप तैयार होते ही वायदा प्राप्त करने के लिए कंप्लीटेशन सर्विस का उपयोग कर सकते हैं और यदि उनमें से कोई एक अपवाद फेंकता है तो प्रोसेसिंग रद्द कर देता है। कुछ इस तरह:

Executor executor = Executors.newFixedThreadPool(4);
CompletionService<SomeResult> completionService = 
       new ExecutorCompletionService<SomeResult>(executor);

//4 tasks
for(int i = 0; i < 4; i++) {
   completionService.submit(new Callable<SomeResult>() {
       public SomeResult call() {
           ...
           return result;
       }
   });
}

int received = 0;
boolean errors = false;

while(received < 4 && !errors) {
      Future<SomeResult> resultFuture = completionService.take(); //blocks if none available
      try {
         SomeResult result = resultFuture.get();
         received ++;
         ... // do something with the result
      }
      catch(Exception e) {
             //log
         errors = true;
      }
}

मुझे लगता है कि यदि आप उनमें से किसी एक को भी त्रुटि देते हैं, तो आप अभी भी किसी भी निष्पादित कार्य को रद्द करने के लिए सुधार कर सकते हैं।


1
: आपके कोड में वही समस्या है जिसका मैंने अपने पोस्ट में उल्लेख किया है। यदि भविष्य में भविष्य में अपवाद होता है, तो कोड अभी भी भविष्य में 1,2,3 के पूरा होने की प्रतीक्षा करेगा। या पूरा कर देगा। सेरसेक) वह भविष्य लौटाएगा जो पहले पूरा होता है?
user93796

1
क्या टाइमआउट के बारे में? क्या मैं एक्स पर अधिकतम सेकंड के लिए प्रतीक्षा करने के लिए पूर्ण सेवा बता सकता हूं?
user93796

1
नहीं होना चाहिए। यह वायदा पर पुनरावृत्ति नहीं करता है, लेकिन जैसे ही कोई तैयार होता है, इसे संसाधित / सत्यापित किया जाता है यदि अपवाद नहीं फेंका जाता है।
dcernahoschi

2
भविष्य की प्रतीक्षा करने के लिए कतार में दिखाई देने के लिए एक पोल (सेकंड) विधि है CompletionService
dcernahoschi

यहाँ
जीथब

107

यदि आप जावा 8 का उपयोग कर रहे हैं तो आप कंप्लीटटेबल सिवनी और कंप्लीटटेबल फाउन्डेशन.ऑलऑफ के साथ यह आसान कर सकते हैं , जो कि सभी सप्लाई कंप्लीटफ्युटुरेट्स के पूरा होने के बाद ही कॉलबैक लागू होता है।

// Waits for *all* futures to complete and returns a list of results.
// If *any* future completes exceptionally then the resulting future will also complete exceptionally.

public static <T> CompletableFuture<List<T>> all(List<CompletableFuture<T>> futures) {
    CompletableFuture[] cfs = futures.toArray(new CompletableFuture[futures.size()]);

    return CompletableFuture.allOf(cfs)
            .thenApply(ignored -> futures.stream()
                                    .map(CompletableFuture::join)
                                    .collect(Collectors.toList())
            );
}

3
हाय @Andrejs, क्या आप कृपया बता सकते हैं कि कोड का यह स्निपेट क्या करता है। मैंने इसे कई स्थानों पर सुझाया है लेकिन वास्तव में जो हो रहा है, उसे लेकर उलझन में हूं। यदि थ्रेड्स में से एक विफल रहता है तो अपवादों को कैसे नियंत्रित किया जाता है?
VSEWHGHP

2
@VSEWHGHP javadoc से: यदि दिए गए कंप्लीटफिटफुट में से कोई भी असाधारण रूप से पूरा हो जाता है, तो कंप्लीट कंपेटिफ़्यूमेंट भी ऐसा करता है, एक कंप्लीशनएक्स अपवाद को इस अपवाद को इसके कारण के रूप में रखता है।
रसियन

1
ठीक है तो मैं उस पर चल रहा था, क्या इस स्निपेट का उपयोग करने का कोई तरीका है लेकिन अन्य सभी थ्रेड्स के लिए मान प्राप्त करते हैं जो सफलतापूर्वक पूरा हो गया है? क्या मुझे कम्प्लीटटेबलफुटर्स लिस्ट पर सिर्फ रिप्लाई करना चाहिए और कॉल को कंप्लीटटेबल सिवनी इग्नोर कर देना चाहिए <लिस्ट <T >> क्योंकि सीक्वेंस फंक्शन में सभी थ्रेड्स रिजल्ट या अपवाद के साथ पूरे होने का ध्यान रखते हैं?
VSEWHGHP

6
यह एक अलग समस्या को हल कर रहा है। यदि आपके पास Futureउदाहरण हैं, तो आप इस विधि को लागू नहीं कर सकते। यह परिवर्तित करने के लिए आसान नहीं है Futureमें CompletableFuture
जारेकसेक

यदि हम किसी कार्य में अपवाद हैं तो यह काम नहीं करेगा।
slisnychyi

21

CompletableFutureजावा 8 में a का प्रयोग करें

    // Kick of multiple, asynchronous lookups
    CompletableFuture<User> page1 = gitHubLookupService.findUser("Test1");
    CompletableFuture<User> page2 = gitHubLookupService.findUser("Test2");
    CompletableFuture<User> page3 = gitHubLookupService.findUser("Test3");

    // Wait until they are all done
    CompletableFuture.allOf(page1,page2,page3).join();

    logger.info("--> " + page1.get());

1
यह स्वीकृत उत्तर होना चाहिए। इसके अलावा यह आधिकारिक स्प्रिंग प्रलेखन का हिस्सा है: spring.io/guides/gs/async-method
maaw

उम्मीद के मुताबिक काम करता है।
Dimon

15

आप ExecutorCompletionService का उपयोग कर सकते हैं । दस्तावेज़ में आपके सटीक उपयोग के लिए एक उदाहरण भी है:

इसके बजाय मान लीजिए कि आप कार्यों के सेट के पहले गैर-अशक्त परिणाम का उपयोग करना चाहते हैं, किसी भी मुठभेड़ अपवाद को अनदेखा करना और पहले एक के तैयार होने पर अन्य सभी कार्यों को रद्द करना:

void solve(Executor e, Collection<Callable<Result>> solvers) throws InterruptedException {
    CompletionService<Result> ecs = new ExecutorCompletionService<Result>(e);
    int n = solvers.size();
    List<Future<Result>> futures = new ArrayList<Future<Result>>(n);
    Result result = null;
    try {
        for (Callable<Result> s : solvers)
            futures.add(ecs.submit(s));
        for (int i = 0; i < n; ++i) {
            try {
                Result r = ecs.take().get();
                if (r != null) {
                    result = r;
                    break;
                }
            } catch (ExecutionException ignore) {
            }
        }
    } finally {
        for (Future<Result> f : futures)
            f.cancel(true);
    }

    if (result != null)
        use(result);
}

यहां ध्यान देने वाली महत्वपूर्ण बात यह है कि ecs.take () को पहले पूरा किया गया कार्य मिलेगा , न कि केवल पहला सबमिट किया गया। इस प्रकार आपको उन्हें निष्पादन को खत्म करने (या एक अपवाद को फेंकने) के क्रम में प्राप्त करना चाहिए।


3

यदि आप जावा 8 का उपयोग कर रहे हैं और CompletableFutureएस में हेरफेर नहीं करना चाहते हैं , तो मैंने एक List<Future<T>>उपयोग की गई स्ट्रीमिंग के लिए परिणाम प्राप्त करने के लिए एक उपकरण लिखा है । कुंजी यह है कि आप map(Future::get)इसे फेंकने के लिए मना किया जाता है।

public final class Futures
{

    private Futures()
    {}

    public static <E> Collector<Future<E>, Collection<E>, List<E>> present()
    {
        return new FutureCollector<>();
    }

    private static class FutureCollector<T> implements Collector<Future<T>, Collection<T>, List<T>>
    {
        private final List<Throwable> exceptions = new LinkedList<>();

        @Override
        public Supplier<Collection<T>> supplier()
        {
            return LinkedList::new;
        }

        @Override
        public BiConsumer<Collection<T>, Future<T>> accumulator()
        {
            return (r, f) -> {
                try
                {
                    r.add(f.get());
                }
                catch (InterruptedException e)
                {}
                catch (ExecutionException e)
                {
                    exceptions.add(e.getCause());
                }
            };
        }

        @Override
        public BinaryOperator<Collection<T>> combiner()
        {
            return (l1, l2) -> {
                l1.addAll(l2);
                return l1;
            };
        }

        @Override
        public Function<Collection<T>, List<T>> finisher()
        {
            return l -> {

                List<T> ret = new ArrayList<>(l);
                if (!exceptions.isEmpty())
                    throw new AggregateException(exceptions, ret);

                return ret;
            };

        }

        @Override
        public Set<java.util.stream.Collector.Characteristics> characteristics()
        {
            return java.util.Collections.emptySet();
        }
    }

इसके लिए AggregateExceptionC # जैसे काम करने की जरूरत है

public class AggregateException extends RuntimeException
{
    /**
     *
     */
    private static final long serialVersionUID = -4477649337710077094L;

    private final List<Throwable> causes;
    private List<?> successfulElements;

    public AggregateException(List<Throwable> causes, List<?> l)
    {
        this.causes = causes;
        successfulElements = l;
    }

    public AggregateException(List<Throwable> causes)
    {
        this.causes = causes;
    }

    @Override
    public synchronized Throwable getCause()
    {
        return this;
    }

    public List<Throwable> getCauses()
    {
        return causes;
    }

    public List<?> getSuccessfulElements()
    {
        return successfulElements;
    }

    public void setSuccessfulElements(List<?> successfulElements)
    {
        this.successfulElements = successfulElements;
    }

}

इस घटक के रूप में सी # के बिल्कुल में कार्य करता है Task.WaitAll । मैं एक ऐसे वेरिएंट पर काम कर रहा हूं, जो CompletableFuture.allOf(समतुल्य Task.WhenAll)

मैंने ऐसा क्यों किया, इसका कारण यह है कि मैं स्प्रिंग का उपयोग कर रहा हूं ListenableFutureऔर इसके CompletableFutureबावजूद अधिक मानक तरीका नहीं है


1
समतुल्य AggregateException की आवश्यकता को देखने के लिए Upvote।
ग्रेनडाकोडर

इस सुविधा का उपयोग करने का एक उदाहरण अच्छा होगा।
XDS

1

इस मामले में कि आप कंप्लीटटेबल फ्यूचर्स की सूची को संयोजित करना चाहते हैं, आप ऐसा कर सकते हैं:

List<CompletableFuture<Void>> futures = new ArrayList<>();
// ... Add futures to this ArrayList of CompletableFutures

// CompletableFuture.allOf() method demand a variadic arguments
// You can use this syntax to pass a List instead
CompletableFuture<Void> allFutures = CompletableFuture.allOf(
            futures.toArray(new CompletableFuture[futures.size()]));

// Wait for all individual CompletableFuture to complete
// All individual CompletableFutures are executed in parallel
allFutures.get();

भविष्य और कंपैटेबल फ़ॉयरफ़ॉर्म, उपयोगी लिंक्स के बारे में अधिक जानकारी के लिए:
1. फ्यूचर: https://www.baeldung.com/java-future
2. कम्प्लीटटेबल फ़ॉइल: https://www.baeldung.com/java-completablefuture
3. कम्प्लीटेबल फ़ॉइल: https : //www.callicoder.com/java-8-completablefuture-tutorial/


0

शायद यह मदद करेगा (कुछ भी नहीं कच्चे धागे के साथ बदल जाएगा, हाँ!) मैं सुझाव देता हूं कि प्रत्येक Futureव्यक्ति को एक अलग धागे के साथ चलाएं (वे समानांतर जाते हैं), फिर जब कभी कोई त्रुटि मिलती है , तो यह प्रबंधक ( Handlerवर्ग) को संकेत देता है ।

class Handler{
//...
private Thread thisThread;
private boolean failed=false;
private Thread[] trds;
public void waitFor(){
  thisThread=Thread.currentThread();
  List<Future<Object>> futures = getFutures();
  trds=new Thread[futures.size()];
  for (int i = 0; i < trds.length; i++) {
    RunTask rt=new RunTask(futures.get(i), this);
    trds[i]=new Thread(rt);
  }
  synchronized (this) {
    for(Thread tx:trds){
      tx.start();
    }  
  }
  for(Thread tx:trds){
    try {tx.join();
    } catch (InterruptedException e) {
      System.out.println("Job failed!");break;
    }
  }if(!failed){System.out.println("Job Done");}
}

private List<Future<Object>> getFutures() {
  return null;
}

public synchronized void cancelOther(){if(failed){return;}
  failed=true;
  for(Thread tx:trds){
    tx.stop();//Deprecated but works here like a boss
  }thisThread.interrupt();
}
//...
}
class RunTask implements Runnable{
private Future f;private Handler h;
public RunTask(Future f,Handler h){this.f=f;this.h=h;}
public void run(){
try{
f.get();//beware about state of working, the stop() method throws ThreadDeath Error at any thread state (unless it blocked by some operation)
}catch(Exception e){System.out.println("Error, stopping other guys...");h.cancelOther();}
catch(Throwable t){System.out.println("Oops, some other guy has stopped working...");}
}
}

मुझे कहना है कि उपरोक्त कोड त्रुटि होगी (जाँच नहीं), लेकिन मुझे उम्मीद है कि मैं समाधान बता सकता हूं। कृपया एक कोशिश है।


0
 /**
     * execute suppliers as future tasks then wait / join for getting results
     * @param functors a supplier(s) to execute
     * @return a list of results
     */
    private List getResultsInFuture(Supplier<?>... functors) {
        CompletableFuture[] futures = stream(functors)
                .map(CompletableFuture::supplyAsync)
                .collect(Collectors.toList())
                .toArray(new CompletableFuture[functors.length]);
        CompletableFuture.allOf(futures).join();
        return stream(futures).map(a-> {
            try {
                return a.get();
            } catch (InterruptedException | ExecutionException e) {
                //logger.error("an error occurred during runtime execution a function",e);
                return null;
            }
        }).collect(Collectors.toList());
    };

0

कंप्लीटेशन सर्विस आपके कॉलबल्स को .submit () विधि के साथ ले जाएगा और आप .take () विधि के साथ गणना को पुनः प्राप्त कर सकते हैं।

एक बात जो आपको नहीं भूलनी चाहिए वह है एक्सशोर सर्विस को .shutdown () विधि कहकर समाप्त करना। इसके अलावा, आप केवल इस पद्धति को कॉल कर सकते हैं जब आपने निष्पादक सेवा के लिए एक संदर्भ सहेजा है, तो एक को रखना सुनिश्चित करें।

उदाहरण कोड - समानांतर में काम करने के लिए एक निश्चित संख्या में कार्य आइटम के लिए:

ExecutorService service = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());

CompletionService<YourCallableImplementor> completionService = 
new ExecutorCompletionService<YourCallableImplementor>(service);

ArrayList<Future<YourCallableImplementor>> futures = new ArrayList<Future<YourCallableImplementor>>();

for (String computeMe : elementsToCompute) {
    futures.add(completionService.submit(new YourCallableImplementor(computeMe)));
}
//now retrieve the futures after computation (auto wait for it)
int received = 0;

while(received < elementsToCompute.size()) {
 Future<YourCallableImplementor> resultFuture = completionService.take(); 
 YourCallableImplementor result = resultFuture.get();
 received ++;
}
//important: shutdown your ExecutorService
service.shutdown();

उदाहरण कोड - समानांतर में काम करने के लिए काम की वस्तुओं की एक गतिशील संख्या के लिए:

public void runIt(){
    ExecutorService service = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
    CompletionService<CallableImplementor> completionService = new ExecutorCompletionService<CallableImplementor>(service);
    ArrayList<Future<CallableImplementor>> futures = new ArrayList<Future<CallableImplementor>>();

    //Initial workload is 8 threads
    for (int i = 0; i < 9; i++) {
        futures.add(completionService.submit(write.new CallableImplementor()));             
    }
    boolean finished = false;
    while (!finished) {
        try {
            Future<CallableImplementor> resultFuture;
            resultFuture = completionService.take();
            CallableImplementor result = resultFuture.get();
            finished = doSomethingWith(result.getResult());
            result.setResult(null);
            result = null;
            resultFuture = null;
            //After work package has been finished create new work package and add it to futures
            futures.add(completionService.submit(write.new CallableImplementor()));
        } catch (InterruptedException | ExecutionException e) {
            //handle interrupted and assert correct thread / work packet count              
        } 
    }

    //important: shutdown your ExecutorService
    service.shutdown();
}

public class CallableImplementor implements Callable{
    boolean result;

    @Override
    public CallableImplementor call() throws Exception {
        //business logic goes here
        return this;
    }

    public boolean getResult() {
        return result;
    }

    public void setResult(boolean result) {
        this.result = result;
    }
}

0

मुझे एक उपयोगिता वर्ग मिला है जिसमें ये शामिल हैं:

@FunctionalInterface
public interface CheckedSupplier<X> {
  X get() throws Throwable;
}

public static <X> Supplier<X> uncheckedSupplier(final CheckedSupplier<X> supplier) {
    return () -> {
        try {
            return supplier.get();
        } catch (final Throwable checkedException) {
            throw new IllegalStateException(checkedException);
        }
    };
}

एक बार आपके पास, एक स्थिर आयात का उपयोग करके, आप इस तरह के सभी वायदा के लिए सरल प्रतीक्षा कर सकते हैं:

futures.stream().forEach(future -> uncheckedSupplier(future::get).get());

आप इस तरह उनके सभी परिणाम भी एकत्र कर सकते हैं:

List<MyResultType> results = futures.stream()
    .map(future -> uncheckedSupplier(future::get).get())
    .collect(Collectors.toList());

बस मेरी पुरानी पोस्ट को फिर से देखना और ध्यान देना कि आपको एक और दुःख था:

लेकिन यहां समस्या यह है कि, उदाहरण के लिए, 4 वें भविष्य एक अपवाद फेंकता है, तो मैं पहले 3 वायदा उपलब्ध होने के लिए अनावश्यक रूप से इंतजार करूंगा।

इस मामले में, सरल समाधान समानांतर में यह करना है:

futures.stream().parallel()
 .forEach(future -> uncheckedSupplier(future::get).get());

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


0
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class Stack2 {   
    public static void waitFor(List<Future<?>> futures) {
        List<Future<?>> futureCopies = new ArrayList<Future<?>>(futures);//contains features for which status has not been completed
        while (!futureCopies.isEmpty()) {//worst case :all task worked without exception, then this method should wait for all tasks
            Iterator<Future<?>> futureCopiesIterator = futureCopies.iterator();
            while (futureCopiesIterator.hasNext()) {
                Future<?> future = futureCopiesIterator.next();
                if (future.isDone()) {//already done
                    futureCopiesIterator.remove();
                    try {
                        future.get();// no longer waiting
                    } catch (InterruptedException e) {
                        //ignore
                        //only happen when current Thread interrupted
                    } catch (ExecutionException e) {
                        Throwable throwable = e.getCause();// real cause of exception
                        futureCopies.forEach(f -> f.cancel(true));//cancel other tasks that not completed
                        return;
                    }
                }
            }
        }
    }
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(3);

        Runnable runnable1 = new Runnable (){
            public void run(){
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                }
            }
        };
        Runnable runnable2 = new Runnable (){
            public void run(){
                try {
                    Thread.sleep(4000);
                } catch (InterruptedException e) {
                }
            }
        };


        Runnable fail = new Runnable (){
            public void run(){
                try {
                    Thread.sleep(1000);
                    throw new RuntimeException("bla bla bla");
                } catch (InterruptedException e) {
                }
            }
        };

        List<Future<?>> futures = Stream.of(runnable1,fail,runnable2)
                .map(executorService::submit)
                .collect(Collectors.toList());

        double start = System.nanoTime();
        waitFor(futures);
        double end = (System.nanoTime()-start)/1e9;
        System.out.println(end +" seconds");

    }
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.