3

したがって、このコード:

int usedPermits = totalPermits - semaphore.availablePermits();
semaphore.release(usedPermits);

2つの行の間に別のスレッドが許可を解放すると、セマフォの容量が実際には元の最大値を超えて増加するため、スレッドセーフではありません。

このコードストリップは1)シングルスレッドであり、2)許可がリリースされる唯一の場所であるため、これは私の状況で機能します。これは、「すべてリリース」と「取得/リリース」が2つの互換性のないデザインパターンであるという事実を単に示している可能性があります。同じオブジェクト上。

ただし、スレッド同期ポリシーがそれほど微妙ではない優先パターンがあるかどうかを確認したいと思います。

4

4 に答える 4

3

他の回答で説明されているように、コードをアトミ​​ックにするための解決策があります。ただし、このコードがすべての状況で正しいとは限らないため、一般的な設定では、問題が解決するという保証はありません。

許可は、それらを使用するアクティビティによって解放されます。その場合、そのコードは不要です。セマフォは自然に補充されるか、そうでない場合は、そのコードが必要になり、安全である限り、変なことは何もしません。

タイムクォンタム(ここでは1分)あたりのアクティビティのレートを制限することを指定していることに注意してください。ただし、アクティビティは1分より長く続く可能性があることを考慮に入れる必要があります。ここで制限できることは、実際には2つあります。

  • クォンタムごとに開始するアクティビティの数、
  • クォンタムで実行されているアクティビティの数

最初のものを制限したい場合は、許可証を補充し、活動家に許可証を保持させるためのコードが必要になります。2番目のケースを処理する場合は、開始時と終了時にそれぞれ許可を取得および解放するようにアクティビティを強制する必要があります。

一部のアクティビティによるセマフォの誤用を恐れている場合は、アクティビティコード自体でのセマフォの使用を禁止してください。実際、レート制限はアクティビティのセマンティクスと完全に直交しているため、その機能をアクティビティのメインコードから分離することをお勧めします。したがって、スケジュールされたアクティビティをコードでラップして、セマフォを処理する必要があります。

class RateLimitedRunnable implements Runnable {
    Runnable runnable;
    RateLimitedRunnable(Runnable r) { runnable = r; }
    void Run() {
        semaphore.acquire();
        runnable.run();
        semaphore.release(); // remove if only limiting starts
    }
}

上記のサンプル(テストされていない)コードは、実際のアクティビティから離れたセマフォの使用の可能な処理を説明しているため、潜在的な誤用を排除します。内部アクティビティがセマフォにアクセスする必要がある場合は、現在の状態を取得するだけで済み、アドホックインターフェイスはその制限されたアクセスを提供するように確実に設計できます。

注:セマフォの使用に関する説明はJavaのコンテキストよりも一般的であるため、ここではスレッドまたはプロセスの意味として「アクティビティ」という用語を使用します。

于 2013-02-20T14:03:55.093 に答える
0

2つの行の間に別のスレッドが許可を解放すると、セマフォの容量が実際には元の最大値を超えて増加するため、スレッドセーフではありません。

これは、によって解決する必要があります。また、すべてがブロック 内にあることsynchronizationを強制します。semaphore.release()synchronized

synchronize(lock) {
    int usedPermits = totalPermits - semaphore.availablePermits();
    semaphore.release(usedPermits);
}

ただし、すべてのブロックを解放しても、afterwordsと呼ばれるスレッドがを超えるため、許可の数がを超えないようにする場合は、これでは不十分な場合があります。totalPermitssynchronized releasesemaphoretotalPermits

直接threads呼び出さずに許可を解放するために呼び出す関数を実装する場合があります。semaphore.release()

void limitedRelease(){
  synchronize(lock) {
    if(semaphore.availablePermits() < totalPermits ) {
      semaphore.release();
    }    
  }  
}
于 2013-02-19T23:36:49.310 に答える
0

セマフォに何かを詰め込むのは、それが実行可能であるとしても、少し奇妙です。また、バースト性が発生します。毎秒10k QPSに制限している場合は、リセットして一度に10000クエリを取得します。既存のRateLimiter実装を使用してみませんか?:

http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/util/concurrent/RateLimiter.html

または、少なくともインスピレーションを得るためにコードを覗いてみてください。

http://code.google.com/p/guava-libraries/source/browse/guava/src/com/google/common/util/concurrent/RateLimiter.java?name=v13.0-rc2

于 2013-02-20T02:02:42.937 に答える
0

標準のSemaphoreクラスを拡張してそのメソッドをオーバーライドし、それらを同期させてみませんか。

public class Semaphore extends java.util.concurrent.Semaphore {
public Semaphore(int permits)
{
    super(permits);
}

public synchronized void releaseAll()
{
    super.release(super.drainPermits());
}   
}
于 2013-09-03T17:46:31.513 に答える