生成される「レコード」の数が決定される Thread を所有するジェネレーター クラスがあり、その数のレコードを生成します (別のスレッドによる取得のために BlockingQueue に配置されます)。
生成されるレコードの数を他のスレッドに知らせたいと思います(とりわけ、賢明な進捗報告のため)。
Future はまさに私が求めているインターフェイスを提供してくれるようですが、私は Java が初めてで、それを実装する慣用的な方法がわかりません。
私のバックグラウンドは C++/Win32 なので、通常は win32 の「イベント」を使用します (によって作成されCreateEvent(0, true, false, 0)
、シグナルSetEvent
とWaitForSingleObject
待機の実装のために)。Javaには があることに気付きましたCountDownLatch
が、これはどういうわけか私が求めているものよりも重く感じられ(本当にブール値が必要なときに int を使用するのと似ています)、この目的には直感的ではないようです(とにかく)。
これが、CountDownLatch と Future を使用した私のコードです。ここで実際のコードを少し要約しました (無関係な実装の詳細を削除し、すべてのエラー処理を無視しています)。
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
public abstract class Generator {
private CountDownLatch numRecordsSignal = new CountDownLatch(1);
private int numRecords;
private BlockingQueue<Record> queue = new LinkedBlockingQueue<Record>();
public Generator() {
new Thread(new Runnable() {
@Override
public void run() {
numRecords = calculateNumRecords();
numRecordsSignal.countDown();
for (Record r : generateRecords()) {
try {
queue.put(r);
} catch (InterruptedException e) {
// [ ... snip ... ]
}
}
}
}).start();
}
public Future<Integer> numRecords() {
return new Future<Integer>() {
// Ignore cancel for now (It wouldn't make sense to cancel
// just this part of the Generator's work, anyway).
public boolean cancel(boolean mayInterruptIfRunning) {
return false;
}
public Integer get() throws InterruptedException {
numRecordsSignal.await();
return numRecords;
}
public Integer get(long timeout, TimeUnit unit)
throws InterruptedException {
numRecordsSignal.await(timeout, unit);
return numRecords;
}
public boolean isCancelled() {
return false;
}
public boolean isDone() {
// Since we can't cancel, just check the state of the
// signal
return numRecordsSignal.getCount() == 0;
}
};
}
public Record nextRecord() throws InterruptedException {
return queue.take();
}
/** --- Boring stuff below this line --- */
public interface Record { }
protected abstract int calculateNumRecords();
protected abstract Iterable<Record> generateRecords();
}
今、私の実際の質問のために:
CountDownLatch
シングルショットシグナリングよりも優れたメカニズムはありますか?- 呼び出し元が結果を待機またはポーリングできるようにしたいのですが、操作をキャンセルできるようにする必要はありません。Future はこのようなものを公開する正しい方法ですか?
- このようなものの中に、特に「非 Java」に見えるものはありますか? 私は完全に間違った道を進んでいますか?
編集:
明確にするために、呼び出し元が次のことができることを期待しています。
Generator gen = new Generator();
Integer numRecords = gen.numRecords().get(); // This call might block waiting for the result
numRecords = gen.numRecords().get(); // This call will never block, as the result is already available.
これは、私が実装しようとしている初期化が遅い値です。「初期化」条件が満たされると、ラッチされます。値が既知になると、値は再評価されません。