java.util.Timer
呼び出しの間に必要な遅延があるため、またはをお勧めしjava.util.concurrent.ScheduledThreadPoolExecutor
ます。は非常にシンプルで、このユースケースTimer
には完全に適しています。ただし、後で追加のスケジューリング要件が特定された場合は、1人ですべてを処理できます。いずれの場合も、固定レート方式ではなく、固定遅延方式を使用してください。Executor
繰り返しタスクは、要求オブジェクトの並行キューをポーリングします。保留中のリクエストがある場合、タスクはそれを実行し、コールバックを介して結果を返します。サービスのクエリと呼び出すコールバックは、リクエストオブジェクトのメンバーです。
アプリケーションは共有キューへの参照を保持します。リクエストをスケジュールするには、リクエストをキューに追加するだけです。
明確にするために、スケジュールされたタスクの実行時にキューが空の場合、要求は行われません。単純なアプローチは、タスクを終了することであり、スケジューラーは1秒後にタスクを呼び出して再度チェックします。
ただし、これは、最近処理された要求がない場合でも、タスクの開始に最大1秒かかる可能性があることを意味します。この不必要な待ち時間が耐えられない場合は、Timer
またはを使用するよりも、独自のスレッドを作成することをお勧めしますScheduledThreadPoolExecutor
。独自のタイミングループでは、リクエストが利用可能になるまで空のキューでブロックすることを選択した場合、スケジューリングをより細かく制御できます。組み込みのタイマーは、前回の実行が終了してから1秒待つことは保証されていません。それらは通常、タスクの開始時間に関連してスケジュールされます。
この2番目のケースがあなたが考えているものである場合、run()
メソッドにはループが含まれます。各反復は、要求が受信されるまでキューをブロックし、時間を記録することから始まります。リクエストを処理した後、時刻が再度チェックされます。時間差が1秒未満の場合は、残りの時間はスリープします。この設定では、あるリクエストの開始から次のリクエストの開始までに1秒の遅延が必要であると想定しています。あるリクエストの終了から次のリクエストまでの間に遅延が必要な場合は、時間を確認する必要はありません。1秒間寝るだけです。
もう1つ注意すべき点は、サービスが1つのリクエストで複数のクエリを受け入れることができる可能性があることです。これによりオーバーヘッドが削減されます。含まれている場合はtake()
、最初の要素をブロックしてpoll()
から、おそらく非常に短いブロック時間(5ミリ秒程度)でを使用して、アプリケーションがそれ以上要求を行っているかどうかを確認することで、これを利用します。その場合、これらはサービスへの単一のリクエストにまとめることができます。の場合queue
、次のBlockingQueue<? extends Request>
ようになります。
Collection<Request> bundle = new ArrayList<Request>();
bundle.add(queue.take());
while (bundle.size() < BUNDLE_MAX) {
Request req = queue.poll(EXTRA, TimeUnit.MILLISECONDS);
if (req == null)
break;
bundle.add(req);
}
/* Now make one service request with contents of "bundle". */