Java फ्यूचर को कंप्लीटटेबल सिवनी में बदलना


95

जावा 8 परिचय देता है CompletableFuture, फ्यूचर का एक नया कार्यान्वयन जो कि कंपोजेबल है (जिसमें xXxx विधियों का एक समूह शामिल है)। मैं इसे विशेष रूप से उपयोग करना चाहता हूं, लेकिन कई पुस्तकालय मैं केवल गैर-कंपोजिट Futureइंस्टेंस का उपयोग करना चाहता हूं ।

वहाँ एक रास्ता है कि मैं इसे रचना कर सकते हैं के Futureअंदर एक लौटे उदाहरणों को लपेटने के CompleteableFutureलिए है?

जवाबों:


57

एक रास्ता है, लेकिन आप इसे पसंद नहीं करेंगे। निम्नलिखित विधि एक Future<T>में बदल देती है CompletableFuture<T>:

public static <T> CompletableFuture<T> makeCompletableFuture(Future<T> future) {
  if (future.isDone())
    return transformDoneFuture(future);
  return CompletableFuture.supplyAsync(() -> {
    try {
      if (!future.isDone())
        awaitFutureIsDoneInForkJoinPool(future);
      return future.get();
    } catch (ExecutionException e) {
      throw new RuntimeException(e);
    } catch (InterruptedException e) {
      // Normally, this should never happen inside ForkJoinPool
      Thread.currentThread().interrupt();
      // Add the following statement if the future doesn't have side effects
      // future.cancel(true);
      throw new RuntimeException(e);
    }
  });
}

private static <T> CompletableFuture<T> transformDoneFuture(Future<T> future) {
  CompletableFuture<T> cf = new CompletableFuture<>();
  T result;
  try {
    result = future.get();
  } catch (Throwable ex) {
    cf.completeExceptionally(ex);
    return cf;
  }
  cf.complete(result);
  return cf;
}

private static void awaitFutureIsDoneInForkJoinPool(Future<?> future)
    throws InterruptedException {
  ForkJoinPool.managedBlock(new ForkJoinPool.ManagedBlocker() {
    @Override public boolean block() throws InterruptedException {
      try {
        future.get();
      } catch (ExecutionException e) {
        throw new RuntimeException(e);
      }
      return true;
    }
    @Override public boolean isReleasable() {
      return future.isDone();
    }
  });
}

जाहिर है, इस दृष्टिकोण के साथ समस्या यह है, कि प्रत्येक के लिए भविष्य , एक सूत्र के परिणाम के लिए इंतजार करना अवरुद्ध हो जाएगा भविष्य वायदा के विचार --contradicting। कुछ मामलों में, बेहतर करना संभव हो सकता है। हालांकि, सामान्य तौर पर, भविष्य के परिणाम के लिए सक्रिय रूप से इंतजार किए बिना कोई समाधान नहीं है ।


1
हा, यही मैंने सोचने से पहले लिखा था कि बेहतर तरीका होना चाहिए। लेकिन, मुझे लगता है कि नहीं
दान Midwood

12
हम्म् ... क्या यह समाधान "सामान्य पूल" के धागे में से एक नहीं खाता है, बस इंतजार करने के लिए? उन "कॉमन पूल" थ्रेड्स को कभी भी ब्लॉक नहीं करना चाहिए ... हम्मम ...
पेटी

1
@ प्रीति: आप सही कह रहे हैं। हालाँकि, मुद्दा यह है, यदि आप सबसे अधिक संभावना है कि आप कुछ गलत कर रहे हैं, भले ही आप सामान्य पूल या अनबाउंड थ्रेड पूल का उपयोग कर रहे हों ।
नोसिड

4
यह सही नहीं हो सकता है, लेकिन CompletableFuture.supplyAsync(supplier, new SinglethreadExecutor())कम से कम उपयोग करने से आम पूल धागे अवरुद्ध नहीं होंगे।
माइकफेह

6
कृपया, बस कभी ऐसा मत करो
Laymain

55

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

    AsynchronousFileChannel open = AsynchronousFileChannel.open(Paths.get("/some/file"));
    // ... 
    CompletableFuture<ByteBuffer> completableFuture = new CompletableFuture<ByteBuffer>();
    open.read(buffer, position, null, new CompletionHandler<Integer, Void>() {
        @Override
        public void completed(Integer result, Void attachment) {
            completableFuture.complete(buffer);
        }

        @Override
        public void failed(Throwable exc, Void attachment) {
            completableFuture.completeExceptionally(exc);
        }
    });
    completableFuture.thenApply(...)

कॉलबैक के बिना केवल दूसरा तरीका जो मैं देख रहा हूं वह यह है कि एक मतदान लूप का उपयोग करना है जो आपके सभी Future.isDone()चेक को एक धागे पर रखता है और फिर फ्यूचर के अनुकूल होने पर जब भी पूरा हो रहा है, तब कॉल करता है।


मैं Apache Http async लाइब्रेरी का उपयोग कर रहा हूं जो FutureCallback को स्वीकार करता है। इसने मेरे जीवन को आसान बना दिया :)
अभिषेक गायकवाड़

13

यदि आपकी विधि (उदाहरण के लिए ) Futureकॉल का परिणाम है , तो सबसे आसान तरीका के बजाय विधि का उपयोग करना होगा ।ExecutorServicesubmit()CompletableFuture.runAsync(Runnable, Executor)

से

Runnbale myTask = ... ;
Future<?> future = myExecutor.submit(myTask);

सेवा

Runnbale myTask = ... ;
CompletableFuture<?> future = CompletableFuture.runAsync(myTask, myExecutor);

CompletableFutureफिर "मूल रूप से" बनाया जाता है।

संपादित करें: @MartinAndersson द्वारा सुधारे गए @SamMefford द्वारा सही टिप्पणियां, यदि आप एक पास करना चाहते हैं, तो आपको Callableकॉल करने की आवश्यकता है , जैसे supplyAsync()कि Callable<T>एक में परिवर्तित करना Supplier<T>, जैसे:

CompletableFuture.supplyAsync(() -> {
    try { return myCallable.call(); }
    catch (Exception ex) { throw new RuntimeException(ex); } // Or return default value
}, myExecutor);

क्योंकि T Callable.call() throws Exception;अपवाद फेंकता है और T Supplier.get();नहीं, आपको अपवाद को पकड़ना होगा ताकि प्रोटोटाइप संगत हो।


1
या, यदि आप Runnable के बजाय Callable <T> का उपयोग कर रहे हैं, तो supplyAsync को आज़माएं:CompletableFuture<T> future = CompletableFuture.supplyAsync(myCallable, myExecutor);
सैम Mefford

@SamMefford धन्यवाद, मैंने उस जानकारी को शामिल करने के लिए संपादन किया।
मैथ्यू

supplyAsyncप्राप्त करता है Supplier। यदि आप पास करने का प्रयास करते हैं तो कोड संकलित नहीं होगा Callable
मार्टिन एंडरसन

@MartinAndersson यह सही है, धन्यवाद। मैं एक में परिवर्तित करने के लिए आगे संपादित Callable<T>करें a Supplier<T>
Matthieu

10

मैंने एक छोटी सी भविष्य की परियोजना प्रकाशित की जो उत्तर में सीधे तरीके से बेहतर बनाने की कोशिश करती है ।

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

उपयोग उदाहरण:

Future oldFuture = ...;
CompletableFuture profit = Futurity.shift(oldFuture);

यह दिलचस्प लग रहा है। क्या यह एक टाइमर धागे का उपयोग कर रहा है? यह कैसे स्वीकार किया गया उत्तर नहीं है?
किरा

@ कियारा हाँ, यह मूल रूप से सभी प्रस्तुत वायदा पर प्रतीक्षा करने के लिए एक टाइमर थ्रेड का उपयोग करता है।
दिमित्री स्पिकाल्स्की

7

सुझाव:

http://www.thedevpiece.com/converting-old-java-future-to-completablefuture/

लेकिन, मूल रूप से:

public class CompletablePromiseContext {
    private static final ScheduledExecutorService SERVICE = Executors.newSingleThreadScheduledExecutor();

    public static void schedule(Runnable r) {
        SERVICE.schedule(r, 1, TimeUnit.MILLISECONDS);
    }
}

और, कंप्लीटेबलप्रोमाइज:

public class CompletablePromise<V> extends CompletableFuture<V> {
    private Future<V> future;

    public CompletablePromise(Future<V> future) {
        this.future = future;
        CompletablePromiseContext.schedule(this::tryToComplete);
    }

    private void tryToComplete() {
        if (future.isDone()) {
            try {
                complete(future.get());
            } catch (InterruptedException e) {
                completeExceptionally(e);
            } catch (ExecutionException e) {
                completeExceptionally(e.getCause());
            }
            return;
        }

        if (future.isCancelled()) {
            cancel(true);
            return;
        }

        CompletablePromiseContext.schedule(this::tryToComplete);
    }
}

उदाहरण:

public class Main {
    public static void main(String[] args) {
        final ExecutorService service = Executors.newSingleThreadExecutor();
        final Future<String> stringFuture = service.submit(() -> "success");
        final CompletableFuture<String> completableFuture = new CompletablePromise<>(stringFuture);

        completableFuture.whenComplete((result, failure) -> {
            System.out.println(result);
        });
    }
}

यह बहुत ही सरल और उपयोगी है और सबसे अधिक उपयोग के मामलों के लिए उपयुक्त है। मैं CompletablePromiseContext नॉट-स्टैटिक बनाऊंगा और चेक अंतराल के लिए परम (जो यहां 1 एमएस पर सेट है) ले जाऊंगा, फिर CompletablePromise<V>कंस्ट्रक्टर को अपने स्वयं CompletablePromiseContextके अलग-अलग (लंबे समय तक) चेक अंतराल को लंबे समय तक चलने के लिए प्रदान करने में सक्षम होना चाहिए Future<V>जहां आप डॉन 't पूरी तरह से खत्म होने पर कॉलबैक (या रचना) चलाने में सक्षम है, और आपके पास (यदि आपके पास कई हैं) CompletablePromiseContextका एक सेट देखने का एक उदाहरण Futureहो सकता है
Dexter Legaspi

5

मुझे एक और सुझाव दें (उम्मीद है, बेहतर) विकल्प: https://github.com/vsilaev/java-async-await/tree/master/com.farata.lang.async.examples/src/main-java/com/farata / समवर्ती

संक्षेप में, विचार निम्नलिखित है:

  1. CompletableTask<V>इंटरफ़ेस का परिचय - के संघ CompletionStage<V>+RunnableFuture<V>
  2. ताना ExecutorServiceवापसी के लिए CompletableTaskसे submit(...)(बजाय तरीकों Future<V>)
  3. हो गया, हमारे पास रननेबल और कंपोज़ेबल फ्यूचर्स हैं।

कार्यान्वयन के लिए एक विकल्प CompletionStage कार्यान्वयन का उपयोग करता (भुगतान ध्यान, CompletionStage बल्कि CompletableFuture से):

उपयोग:

J8ExecutorService exec = J8Executors.newCachedThreadPool();
CompletionStage<String> = exec
   .submit( someCallableA )
   .thenCombineAsync( exec.submit(someCallableB), (a, b) -> a + " " + b)
   .thenCombine( exec.submit(someCallableC), (ab, b) -> ab + " " + c); 

2
छोटा अद्यतन: कोड को अलग प्रोजेक्ट में ले जाया जाता है, github.com/vsilaev/tascalate-concurrent , और अब java.util.concurrent से आउट-ऑफ-द-वे बॉक्स Executor-s का उपयोग करना संभव है।
वलेरी सिलैव जुएल
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.