জাভা ফিউচারকে কমপ্লিটেবল ফিউচারে রূপান্তর করুন


97

জাভা 8 প্রবর্তন করেছে CompletableFuture, ভবিষ্যতের একটি নতুন বাস্তবায়ন যা কমপোজযোগ্য (তারপরে এক্সএক্সএক্সএক্স পদ্ধতিগুলির একটি গুচ্ছ অন্তর্ভুক্ত)। আমি এটি একচেটিয়াভাবে ব্যবহার করতে চাই, তবে আমি যে লাইব্রেরিগুলি ব্যবহার করতে চাই তার মধ্যে অনেকগুলি কেবল অ-কম্পোজেবল 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();
    }
  });
}

স্পষ্টতই, এই পদ্ধতির সাথে সমস্যাটি হ'ল, প্রতিটি ভবিষ্যতের জন্য , ফিউচারের ধারণার সাথে চুক্তি করে ভবিষ্যতের ফলাফলের জন্য অপেক্ষা করতে একটি থ্রেড ব্লক করা হবে । কিছু ক্ষেত্রে, আরও ভাল করা সম্ভব হতে পারে। তবে, সাধারণভাবে, ভবিষ্যতের ফলাফলের জন্য সক্রিয়ভাবে অপেক্ষা না করে কোনও সমাধান নেই ।


4
হ্যাঁ, আরও ভাল উপায় থাকতে হবে তা ভেবে আমি ঠিক এটিই লিখেছিলাম। তবে, আমি অনুমান করি না
ড্যান মিডউড 20

12
হুমম ... এই সমাধানটি "কমন পুল" এর একটি থ্রেড খায় না, কেবল অপেক্ষা করার জন্য? এই "সাধারণ পুল" থ্রেডগুলি কখনই ব্লক করা উচিত নয় ... হুঁ মিম ...
পেটি

4
@ পেটি: আপনি ঠিক বলেছেন। তবে মুল বক্তব্যটি হ'ল আপনি যদি সাধারণ পুল বা আনবাউন্ডেড থ্রেড পুল ব্যবহার করছেন তবে নির্বিশেষে যদি আপনি সম্ভবত কিছু ভুল করছেন ।
nosid

4
এটি নিখুঁত নাও হতে পারে তবে ব্যবহারের CompletableFuture.supplyAsync(supplier, new SinglethreadExecutor())ফলে কমপক্ষে সাধারণ পুলের থ্রেডগুলিতে অবরুদ্ধ হবে না।
মাইকএফহে

6
দয়া করে, কখনও এটি করবেন না
লেইমেন

56

আপনি যে লাইব্রেরিটি ব্যবহার করতে চান তা যদি ফিউচার স্টাইলের পাশাপাশি একটি কলব্যাক শৈলী পদ্ধতিও সরবরাহ করে তবে আপনি এটিকে এমন কোনও হ্যান্ডলার সরবরাহ করতে পারেন যা কোনও অতিরিক্ত থ্রেড ব্লকিং ছাড়াই কমপ্লেটেবল ফিউচার সম্পূর্ণ করে। তাই ভালো:

    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()চেক একক থ্রেডে রাখে এবং তারপরে যখন কোনও ফিউচার পাওয়ার যোগ্য হয় তখনই সম্পূর্ণ অনুরোধ জানানো হয়।


4
আমি অ্যাপাচি এইচটিটিপি অ্যাসিঙ্ক লাইব্রেরি ব্যবহার করছি যা ফিউচারক্যালব্যাক গ্রহণ করে। এটি আমার জীবনকে সহজ করে দিয়েছে :)
অভিষেক গায়কওয়াদ

14

যদি আপনার Futureকোনও ExecutorServiceপদ্ধতিতে কল করার ফলাফল হয় (উদাঃ submit()), তবে তার CompletableFuture.runAsync(Runnable, Executor)পরিবর্তে পদ্ধতিটি ব্যবহার করা সবচেয়ে সহজ ।

থেকে

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

প্রতি

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

CompletableFutureতারপর "নেটিভ" তৈরি করা হয়।

সম্পাদনা করুন: @SamMefford মন্তব্য অনুধাবন @MartinAndersson সংশোধন, যদি আপনি একটি প্রেরণ করতে ইচ্ছুক 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();করে এবং না করে, আপনাকে অবশ্যই ব্যতিক্রমটি ধরতে হবে যাতে প্রোটোটাইপগুলি সামঞ্জস্যপূর্ণ।


4
অথবা, যদি আপনি কলযোগ্য <T> ব্যবহারযোগ্য না হয়ে রান্নেবলের পরিবর্তে সরবরাহের চেষ্টা করুন অ্যাসিঙ্ক:CompletableFuture<T> future = CompletableFuture.supplyAsync(myCallable, myExecutor);
স্যাম মেফর্ড

@ সামমফর্ড ধন্যবাদ, আমি তথ্যটি অন্তর্ভুক্ত করার জন্য সম্পাদনা করেছি।
ম্যাথিউউ

supplyAsyncপ্রাপ্ত a Supplier। আপনি একটিতে পাস করার চেষ্টা করলে কোডটি সংকলন করবে না Callable
মার্টিন অ্যান্ডারসন

@ মার্টিনএন্ডারসন ঠিক আছে, ধন্যবাদ আমি একটি রূপান্তর করতে আরও সম্পাদিত Callable<T>একটি থেকে Supplier<T>
ম্যাথিউউ

10

আমি একটি সামান্য ফিউচারিটি প্রকল্প প্রকাশ করেছি যা উত্তরের সরল পথের চেয়ে আরও ভাল করার চেষ্টা করে ।

মূল ধারণাটি হ'ল অভ্যন্তরের সমস্ত ফিউচার রাজ্যগুলি যাচাই করার জন্য কেবল একটি থ্রেড (এবং অবশ্যই কেবল একটি স্পিন লুপ নয়) ব্যবহার করা যা প্রতিটি ফিউচার -> কমপ্লেটেবলফিউচার রূপান্তরের জন্য একটি পুল থেকে একটি থ্রেড ব্লক করা এড়াতে সহায়তা করে।

ব্যবহারের উদাহরণ:

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

এটি আকর্ষণীয় দেখায়। এটি কি টাইমার থ্রেড ব্যবহার করছে? এটি কীভাবে গৃহীত উত্তর নয়?
কিরা

@ কীরা হ্যাঁ, এটি সমস্ত জমা দেওয়া ফিউচারের জন্য অপেক্ষা করতে মূলত একটি টাইমার থ্রেড ব্যবহার করে।
দিমিত্রি স্পিখালস্কি

8

পরামর্শ:

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);
    }
}

এবং, CompletablePromise:

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);
        });
    }
}

& মার্জিত এবং বেশিরভাগ ব্যবহারের ক্ষেত্রে ফিট করে reason আমি তৈরি করতে পারতাম CompletablePromiseContext না স্ট্যাটিক এবং চেক বিরতি জন্য PARAM (যা এখানে 1 MS সেট করা হয়) নিতে তারপর জমিদার CompletablePromise<V>কন্সট্রাকটর আপনার নিজের প্রদান পাবে CompletablePromiseContextদীর্ঘক্ষন ধরে চলা একটি সম্ভবত পৃথক (আর) পরীক্ষা ব্যবধান সঙ্গে Future<V>আপনি কোথায় ডন 'সমাপ্তির সাথে সাথে কলব্যাক (বা রচনা) চালানোর জন্য পুরোপুরি সক্ষম হতে হবে না এবং আপনার CompletablePromiseContextএকটি সেটও দেখার একটি উদাহরণ থাকতে পারে Future(আপনার অনেকের ক্ষেত্রে রয়েছে)
ডেক্সটার লেগ্যাস্পি

5

আমাকে আরেকটি (আশা করি, আরও ভাল) বিকল্পটি প্রস্তাব দিন: https://github.com/vsilaev/java-async-await/tree/master/com.farata.lang.async.example/src/main/java/com/farata / একযোগে

সংক্ষেপে, ধারণাটি নিম্নলিখিত:

  1. CompletableTask<V>ইন্টারফেস পরিচয় - CompletionStage<V>+ এর ইউনিয়ন RunnableFuture<V>
  2. পদ্ধতি থেকে ExecutorServiceফিরে ওয়ার্প (পরিবর্তে )CompletableTasksubmit(...)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); 

4
ছোট আপডেট: কোডটি পৃথক প্রকল্পে স্থানান্তরিত করা হবে, github.com/vsilaev/tascalate-concurrent , এবং এখন java.util.concurrent থেকে বাইরের উপায় এক্সিকিউটার-এস ব্যবহার করা সম্ভব।
ভ্যালারি সিলাভ
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.