[英]Java “in-order” semaphore
對於如何解決它,我有一個問題和一個模糊的想法,但是我會嘗試在上下文中進行過多共享以避免XY問題 。
我有一個異步方法,該方法立即返回一個番石榴ListenableFuture
,我需要調用它數十萬或數百萬次(將來本身可能需要一些時間才能完成)。 我真的不能更改該方法的內部。 內部涉及一些嚴重的資源爭用,因此我想限制一次發生的對該方法的調用次數。 所以我嘗試使用Semaphore
:
public class ConcurrentCallsLimiter<In, Out> {
private final Function<In, ListenableFuture<Out>> fn;
private final Semaphore semaphore;
private final Executor releasingExecutor;
public ConcurrentCallsLimiter(
int limit, Executor releasingExecutor,
Function<In, ListenableFuture<Out>> fn) {
this.semaphore = new Semaphore(limit);
this.fn = fn;
this.releasingExecutor = releasingExecutor;
}
public ListenableFuture<Out> apply(In in) throws InterruptedException {
semaphore.acquire();
ListenableFuture<Out> result = fn.apply(in);
result.addListener(() -> semaphore.release(), releasingExecutor);
return result;
}
}
因此,我可以將我的調用包裝在此類中,然后調用它:
ConcurrentLimiter<Foo, Bar> cl =
new ConcurrentLimiter(10, executor, someService::turnFooIntoBar);
for (Foo foo : foos) {
ListenableFuture<Bar> bar = cl.apply(foo);
// handle bar (no pun intended)
}
這種類型的作品 。 問題在於尾部延遲確實很差。 一些調用變得“不幸”,並最終花費很長時間嘗試在該方法調用中獲取資源。 某些內部的指數退避邏輯使這種情況更加惡化,與較急於等待更短時間再嘗試的新呼叫相比,不幸的呼叫獲得所需資源的機會越來越少。
解決該問題的理想方法是,如果有類似該信號量但具有順序的概念。 例如,如果限制為10,則當前的第11個呼叫必須等待前10個呼叫中的任何一個完成。 我想要的是第11個通話必須等待第一個通話完成。 這樣,“不幸”的電話就不會繼續被不斷涌入的新電話所困擾。
似乎我可以為調用分配一個整數序列號,並以某種方式跟蹤尚未完成的最低序列號,但無法完全了解如何完成該工作,尤其是因為AtomicInteger
上實際上沒有任何有用的“等待”方法管他呢。
您可以使用公平參數創建信號量:
Semaphore(int permits, boolean fair)
// fair-如果此信號量將確保在爭用條件下先進先出授予許可,則為true,否則為false
我想到的一個不理想的解決方案是使用Queue
存儲所有期貨:
public class ConcurrentCallsLimiter<In, Out> {
private final Function<In, ListenableFuture<Out>> fn;
private final int limit;
private final Queue<ListenableFuture<Out>> queue = new ArrayDeque<>();
public ConcurrentCallsLimiter(int limit, Function<In, ListenableFuture<Out>> fn) {
this.limit = limit;
this.fn = fn;
}
public ListenableFuture<Out> apply(In in) throws InterruptedException {
if (queue.size() == limit) {
queue.remove().get();
}
ListenableFuture<Out> result = fn.apply(in);
queue.add(result);
return result;
}
}
但是,這似乎是對內存的極大浪費。 涉及的對象可能有點大,並且可以將限制設置得很高。 因此,我對沒有O(n)內存使用情況的更好答案持開放態度。 似乎它應該在O(1)中可行。
“如果有類似於那個信號量的東西,但是有秩序的概念。” 存在這樣的野獸。 這稱為Blocking queue
。 創建一個這樣的隊列,在其中放置10個項目,然后使用take
而不是acquire
和put
而不是release
。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.