簡體   English   中英

將 ExecutorService 與立即 get() 一起使用有什么好處?

[英]What's the advantage of using ExecutorService with immediate get()?

我在這里的代碼中看到了很多

executorService.submit(() -> {
    // do stuff
}).get();

...我想知道為什么 executorService 會這樣使用,提交你會立即得到的東西?

因為有人盲目地從Javadoc復制了這個,上面寫着“如果你想立即阻止等待任務,你可以使用 result = exec.submit(aCallable).get(); 形式的結構”

除了@Michael在他對該問題的評論中提到的異常語義的細微差別之外,wrt也存在細微差別。 線程中斷:如果// do stuff阻塞,您仍然可以中斷對Future.get的阻塞調用。

提交您將立即得到的東西

僅僅因為您提交任務並立即“獲取”它,並不一定意味着它將立即由 ExecutorService 運行。

這是一個有用的實際示例:

您有一個 web 服務器,它返回天氣數據。 當收到 HTTP 請求時,您需要連接到天氣 API 以檢索天氣數據。

但是,這種天氣 API 只允許兩個並發連接。

解決此問題的一種方法是使用只有兩個可用線程的 ExecutorService。

現在,無論有多少 servlet 線程同時提交任務執行,都可以確保一次只有兩個線程可以針對天氣 API 執行請求。 servlet 線程將阻塞,直到天氣 API 可以為 servlet 提供請求的數據。

調用立即get()強制ExecutorService在執行程序服務的線程池中而不是本地線程中處理“做事”,例如,如果分配給它的最大線程數很小,盡管有很多線程,這可能會阻塞處理運行以處理請求的線程。

考慮一個只有 1 個線程的 executor 服務的明顯示例:

ExecutorService executorService = Executors.newSingleThreadExecutor();

以及從HttpRequestHandler調用的示例代碼。

通過使用“do stuff”的立即get()處理將被序列化,盡管有許多同時請求正在各自的線程中進行處理。

如果沒有executorService.submit(...).get()的包裝,“do stuff”的處理將在請求處理程序的線程中並行完成。

或通過使用:

ExecutorService executorService = Executors.newFixedThreadPool(3);

盡管同時處理了大量請求,但您將處理的並行度限制為(例如)3 個線程。


其他更微妙的效果可以通過選擇運行“do stuff”的Thread來實現。 考慮:

ExecutorService executorService = Executors.newSingleThreadExecutor(myThreadFactory);

將使“做事”在自定義類型的線程中運行,而不是在為HttpRequestHandler選擇的線程類型中運行。

我能想到的唯一用例是:

  • 調用它的代碼必須同步返回一些結果(即它正在實現一些同步 api 處理 http 請求或諸如此類)
  • 被調用的 executor 是 ForkJoinPool ,提交的任務是 RecursiveTask ,它將在內部分叉。 這樣您就可以使用多個 cpu 來執行整個任務。

Future上使用.get()調用可以抽象出一個人如何准確地傳遞結果的細節。 原諒示例的長度,如果查看以下 class 的.get()方法,可以看到在調用線程中實現相同類型的計時機制將是過多的樣板代碼。 通過抽象它,調用線程將簡單地無限期阻塞,而工作線程則擔心在其Future的 promise 上交付的細節

import java.util.Optional;
import java.util.concurrent.*;
import java.util.concurrent.locks.ReentrantLock;

class DecayingRetry<T> implements Future<T> {
    protected final ScheduledExecutorService executor;
    protected final Callable<T> callable;
    private final boolean isOwnExecutor;
    protected ScheduledFuture<?> future;
    @SuppressWarnings("OptionalUsedAsFieldOrParameterType")
    protected Optional<?> result;
    protected boolean isDone;
    protected boolean isCancelled;
    protected final ReentrantLock lock;
    private final double maxDelay;
    private double initialDelay;
    private final TimeUnit timeUnit;


    private DecayingRetry(ScheduledExecutorService executor,
                            boolean isOwnExecutor,
                            Callable<T> callable,
                            long initialDelay,
                            long maxDelay,
                            TimeUnit timeUnit) {
        this.isOwnExecutor = isOwnExecutor;
        lock = new ReentrantLock(true);
        lock.lock();
        this.executor = executor;
        this.callable = callable;
        isCancelled = false;
        isDone = false;
        if (maxDelay < 0) {
            this.maxDelay = Double.POSITIVE_INFINITY;
        } else {
            this.maxDelay = maxDelay;
        }
        lock.lock();
        this.initialDelay = (double) initialDelay;
        this.timeUnit = timeUnit;
        future = executor.schedule(this::delayLoop, initialDelay, timeUnit);

    }


    public static <T> T on(Callable<T> callable, long initialDelay, long maxDelay, TimeUnit timeUnit) throws Exception {
        try {
            return Optional.ofNullable(callable.call()).orElseThrow(IllegalStateException::new);
        } catch (IllegalStateException ignored) {
            try {
                return new DecayingRetry<>(Executors.newSingleThreadScheduledExecutor(),
                                                      true,
                                                      callable,
                                                      initialDelay,
                                                      maxDelay,
                                                      timeUnit).get();
            } catch (InterruptedException | ExecutionException e) {
                e.printStackTrace();
                System.exit(-1);
            }
        }
        return null;
    }

    public static <T> T on(Callable<T> callable,
                           ScheduledExecutorService executor,
                           long initialDelay,
                           long maxDelay,
                           TimeUnit timeUnit) throws Exception {
        try {
            return Optional.ofNullable(callable.call()).orElseThrow(IllegalStateException::new);
        } catch (IllegalStateException ignored) {
            try {
                return new DecayingRetry<>(executor,
                                                      false,
                                                      callable,
                                                      initialDelay,
                                                      maxDelay,
                                                      timeUnit).get();
            } catch (InterruptedException | ExecutionException e) {
                e.printStackTrace();
                System.exit(-1);
            }
        }
        return null;
    }

    synchronized private void delayLoop() {
        if (isDone) {
            return;
        }
        try {
            result = Optional.ofNullable(callable.call());
        } catch (Exception e) {
            result = Optional.of(e);
            isDone = true;
            return;
        }
        if (!result.isPresent()) {
            if (initialDelay < maxDelay) {
                initialDelay *= 1.618033988749; //PHI
                initialDelay = Math.min(maxDelay, initialDelay);
            }
            future = executor.schedule(this::delayLoop, (long) initialDelay, timeUnit);
        } else {
            isDone = true;
            lock.unlock();
        }
    }


    public boolean cancel(boolean mayInterruptIfRunning) {
        if (isDone) {
            return false;
        } else if (future.cancel(mayInterruptIfRunning)) {
            isCancelled = true;
            isDone = true;
            return true;
        }
        return false;
    }

    @Override
    public boolean isCancelled() {
        return isCancelled;
    }

    @Override
    public boolean isDone() {
        return isDone;
    }

    @Override
    @NotNull
    public T get() throws InterruptedException, ExecutionException {
        lock.lock();
        while (!isDone) { // lock acquired too early for some reason, so we allow the worker thread to grab it
            lock.unlock();
            lock.lock();
        }
        if (result.isPresent()) {
            if (result.get() instanceof Throwable) {
                throw new ExecutionException((Throwable) result.get());
            }
            if (isOwnExecutor) {
                executor.shutdown();
            }
            //noinspection unchecked
            return (T) result.get();
        }
        throw new ExecutionException(new IllegalStateException("Retry result was null"));
    }

    public T get(long timeout, @NotNull TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
        throw new ExecutionException(new IllegalStateException("Not implemented"));
    }

}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM