PriorityBlockingQueue
は無制限ですが、何らかの方法でバインドする必要があります。それを達成するための最良の方法は何ですか?
参考までに、境界PriorityBlockingQueue
は で使用されますThreadPoolExecutor
。
NB: 例外が発生した場合に例外をスローしたくない場合は、オブジェクトをキューに入れてから、優先度の値に基づいてカットしたいと考えています。このカットを行う良い方法はありますか?
PriorityBlockingQueue
は無制限ですが、何らかの方法でバインドする必要があります。それを達成するための最良の方法は何ですか?
参考までに、境界PriorityBlockingQueue
は で使用されますThreadPoolExecutor
。
NB: 例外が発生した場合に例外をスローしたくない場合は、オブジェクトをキューに入れてから、優先度の値に基づいてカットしたいと考えています。このカットを行う良い方法はありますか?
私は実際にそれをサブクラス化しません。現時点ではサンプル コードをまとめることはできませんが、デコレータ パターンのバージョンを提案したいと思います。
新しいクラスを作成し、対象のクラスによって実装されるインターフェイスを実装します: PriorityBlockingQueue。このクラスで使用される次のインターフェイスが見つかりました。
Serializable, Iterable<E>, Collection<E>, BlockingQueue<E>, Queue<E>
クラスのコンストラクターでは、PriorityBlockingQueue
a をコンストラクター パラメーターとして受け入れます。
次に、 のインスタンスを介して、インターフェイスに必要なすべてのメソッドを実装しますPriorityblockingQueue
。Bounded にするために必要なコードを追加します。これは、Decorator パターンのかなり標準的な実装です。
Google Collections/Guavaライブラリにこれの実装があります: MinMaxPriorityQueue。
min-max プライオリティ キューは、最大サイズで設定できます。その場合、キューのサイズがその値を超えるたびに、キューはコンパレータに従って最大の要素 (追加されたばかりの要素である可能性があります) を自動的に削除します。これは、いっぱいになると新しい要素をブロックまたは拒否する従来の制限付きキューとは異なります。
頭のてっぺんから、それをサブクラス化し、put メソッドを上書きしてこれを強制します。超過した場合は、例外をスローするか、適切と思われることを行います。
何かのようなもの:
public class LimitedPBQ extends PriorityBlockingQueue {
private int maxItems;
public LimitedPBQ(int maxItems){
this.maxItems = maxItems;
}
@Override
public boolean offer(Object e) {
boolean success = super.offer(e);
if(!success){
return false;
} else if (this.size()>maxItems){
// Need to drop last item in queue
// The array is not guaranteed to be in order,
// so you should sort it to be sure, even though Sun's Java 6
// version will return it in order
this.remove(this.toArray()[this.size()-1]);
}
return true;
}
}
編集: add と put の両方で呼び出しオファーがあるため、それをオーバーライドするだけで十分です
編集 2: maxItems を超える場合は、最後の要素を削除する必要があります。ただし、もっとエレガントな方法があるかもしれません。
Frank V の提案に従って BoundedPriorityBlockingQueue を実装した後、私はそれが私が望んでいたことをまったくしていないことに気付きました。主な問題は、キューに挿入する必要がある項目が、既にキューにあるすべてのものよりも優先度が高い可能性があることです。したがって、私が本当に欲しいのは「ピボット」メソッドです。オブジェクトをキューに入れると、キューがいっぱいになったときに、ブロックするのではなく、優先度の最も低いオブジェクトを取得したいと考えています。
Frank V の提案を具体化するために、次の断片を使用しました...
public class BoundedPriorityBlockingQueue<E>
implements
Serializable,
Iterable<E>,
Collection<E>,
BlockingQueue<E>,
Queue<E>,
InstrumentedQueue
{
... プライベートな最終的な ReentrantLock ロック; // = 新しい ReentrantLock(); プライベート最終条件 notFull;
final private int capacity;
final private PriorityBlockingQueue<E> queue;
public BoundedPriorityBlockingQueue(int capacity)
throws IllegalArgumentException,
NoSuchFieldException,
IllegalAccessException
{
if (capacity < 1) throw
new IllegalArgumentException("capacity must be greater than zero");
this.capacity = capacity;
this.queue = new PriorityBlockingQueue<E>();
// gaining access to private field
Field reqField;
try {
reqField = PriorityBlockingQueue.class.getDeclaredField("lock");
reqField.setAccessible(true);
this.lock = (ReentrantLock)reqField.get(ReentrantLock.class);
this.notFull = this.lock.newCondition();
} catch (SecurityException ex) {
ex.printStackTrace();
throw ex;
} catch (NoSuchFieldException ex) {
ex.printStackTrace();
throw ex;
} catch (IllegalAccessException ex) {
ex.printStackTrace();
throw ex;
}
...
@Override
public boolean offer(E e) {
this.lock.lock();
try {
while (this.size() == this.capacity)
notFull.await();
boolean success = this.queue.offer(e);
return success;
} catch (InterruptedException ie) {
notFull.signal(); // propagate to a non-interrupted thread
return false;
} finally {
this.lock.unlock();
}
}
...
これにはいくつかのインストルメンテーションも含まれているため、キューの有効性を確認できます。私はまだ「PivotPriorityBlockingQueue」に取り組んでいます。興味がある人は投稿できます。
ConcurrencyUtils リポジトリに実装があります。
これまでのところ、次のすべての特性を備えた単一の回答はありません。
BlockingQueue
ます。残念ながら、BlockingQueue
標準の Java ライブラリには実装がありません。実装を見つけるか、自分で何かを実装する必要があります。を実装するBlockingQueue
には、適切なロックに関する知識が必要です。
ここに私が提案するものがあります: https://gist.github.com/JensRantil/30f812dd237039257a3dを見て、それをテンプレートとして使用して、 a の周りに独自のラッパーを実装しますSortedSet
。基本的に、すべてのロックがそこにあり、複数の単体テストがあります (微調整が必要です)。
実行したい Runnables の順序が厳密でない場合 (そのまま: 優先度の高いタスクが存在するにもかかわらず、優先度の低いタスクが実行される場合があります)、次のことをお勧めします。サイズダウン:
if (queue.size() > MIN_RETAIN * 2){
ArrayList<T> toRetain = new ArrayList<T>(MIN_RETAIN);
queue.drainTo(toRetain, MIN_RETAIN);
queue.clear();
for (T t : toRetain){
queue.offer(t);
}
}
優先度の低いタスクが同時アクセスを使用してキューから取得されると、ドレインが瞬間につながるため、順序を厳密にする必要がある場合、これは明らかに失敗します。
利点は、これがスレッド セーフであり、プライオリティ キューの設計と同じくらい高速になる可能性が高いことです。
Google Collections APIのForwardingQueueを見てください。セマティクスをブロックするには、 Semaphoreを使用できます。
ここに別の実装があります
あなたが求めていることをしているようです:
BoundedPriorityQueue は、要素数に上限がある優先キューを実装します。キューがいっぱいでない場合、追加された要素は常に追加されます。キューがいっぱいで、追加された要素がキュー内の最小の要素よりも大きい場合、最小の要素が削除され、新しい要素が追加されます。キューがいっぱいで、追加された要素がキュー内の最小要素より大きくない場合、新しい要素は追加されません。