簡體   English   中英

Java“有序”信號燈

[英]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而不是acquireput而不是release

暫無
暫無

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

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