4

問題: 修正レートで dao メソッドを 10 秒ごとに呼び出す必要があるため、結果が有効かどうかを確認する必要があります。タイムアウト (たとえば 2 分) が終了しました。

アプローチ: タスクとスケジューラ ロジックを分離しておき、同様の要件を持つさまざまなクラスで使用できるようにタスクを記述したいと考えています。

私が考えることができる 1 つの方法は、新しいポーラー タスクを定義することです。

public abstract class PollerTask<T> implements Runnable {

    abstract public boolean isValid(T result);

    abstract public T task();

    private T result;

    private volatile boolean complete;

    public boolean isComplete() {
        return complete;
    }

    public T getResult() {
        return result;
    }

    @Override
    final public void run() {
        result = task();
        if (complete = isValid(result)) {
            //may be stop scheduler ??
        }

    }
}

ユーザーは単にタスクと isValid の実装を提供する必要があります。

次に、プーリングの頻度とタイムアウトを取り、スケジュールされたエグゼキューターを作成してこのタスクを送信する別のクラスを定義できます

public class PollerTaskExecutor {

    private int pollingFreq;
    private int timeout;
    private ScheduledExecutorService executor;
    private ScheduledExecutorService terminator;
    private ExecutorService condition;
    private volatile boolean done;
    private ScheduledFuture future;

    public PollerTaskExecutor(int pollingFreq, int timeout) {
        this.pollingFreq = pollingFreq;
        this.timeout = timeout;
        executor = Executors.newSingleThreadScheduledExecutor();
        terminator = Executors.newSingleThreadScheduledExecutor();
        condition = Executors.newSingleThreadExecutor();
    }

    public void submitTaskForPolling(final PollerTask pollerTask) {
        future = executor.scheduleAtFixedRate(pollerTask, 0, pollingFreq, TimeUnit.SECONDS);
        terminator.schedule(new Runnable() {
            @Override
            public void run() {
                complete();
            }
        }, timeout, TimeUnit.SECONDS);
        condition.execute(new Runnable() {
            @Override
            public void run() {
                if (pollerTask.isComplete()) {
                    complete();
                }
            }
        });

    }

    public boolean isDone() {
        return done;
    }

    public void complete() {
        future.cancel(false);
        executor.shutdown();
        terminator.shutdown();
        condition.shutdown();
        done = true;

    }

ユーザーは pollerExecutor.isDone が true を返すまで待機して結果を取得できるようになりました。次の目的で 3 つのエグゼキューターを使用する必要がありました。

  1. 固定間隔でタスクを実行するエグゼキュータ
  2. タイムアウトが終了したときにすべてを停止するエグゼキュータ
  3. タイムアウト前に有効な結果が得られた場合、executor はすべて停止します。

誰かがより良いアプローチを提案できますか?

4

2 に答える 2

3

セルフスケジューリングタスクにしましょう。擬似コード:

public class PollingTaskRunner {

...
CountDownLatch doneWait = new CountDownLatch(1);
volatile boolean done;

PollingTaskRunner(Runnable pollingTask, int frequency, int period) {
    ...
    endTime = now + period;
    executor.schedule(this, 0);
}

run() {

    try {
        pollingTask.run();
    } catch (Exception e) {
        ...
    }
    if (pollingTask.isComplete() || now + frequency > endTime) {
        done = true;
        doneWait.countDown();
        executor.shutdown();
    } else {
        executor.schedule(this, frequency);
    }
}

await() {
    doneWait.await();
}

isDone() {
    return done;
}
}

それほど複雑ではありませんが、これを初めて実行/テストするときに多くのデバッグステートメントを追加して、何が起こっているのかを理解してください。意図したとおりに実行されると、パターンを簡単に再利用できます。

于 2014-04-22T22:39:56.180 に答える
2

少し単純な方法です。ターミネーター用の別のエグゼキューター サービスは必要ありません。ターミネーター タスクを同じエグゼキューターにプッシュするだけで済みます。

さらに簡単です。そのPollerTask結果を に配置しBlockingQueueます。次に、PollingTaskRunnerその時間pollを計りBlockingQueueます。タスクが成功またはタイムアウトしたためにpoll呼び出しから制御が返されたとき。ScheduledFuture.cancel

于 2014-04-22T23:52:32.480 に答える