5

私の読書から、ScheduledExecutorService は、Java でタイマーを開始および停止する正しい方法のようです。

タイマーを開始および停止するコードを移植する必要があります。これは定期的なタイマーではありません。このコードは、タイマーを開始する前に停止します。したがって、事実上すべての開始は実際には restart() です。ScheduledExecutorService を使用してこれを行う正しい方法を探しています。これが私が思いついたものです。私が見逃していることについてのコメントと洞察を探しています:

ScheduledExecutorService _Timer = Executors.newScheduledThreadPool(1);
ScheduledFuture<?> _TimerFuture = null;

private boolean startTimer() {
    try {
        if (_TimerFuture != null) {
            //cancel execution of the future task (TimerPopTask())
            //If task is already running, do not interrupt it.
            _TimerFuture.cancel(false);
        }

        _TimerFuture = _Timer.schedule(new TimerPopTask(), 
                                       TIMER_IN_SECONDS, 
                                       TimeUnit.SECONDS);
        return true;
    } catch (Exception e) {
        return false;
    }
}

private boolean stopTimer() {
    try {
        if (_TimerFuture != null) {
            //cancel execution of the future task (TimerPopTask())
            //If task is already running, interrupt it here.
            _TimerFuture.cancel(true);
        }

        return true;
    } catch (Exception e) {
        return false;
    }
}

private class TimerPopTask implements Runnable  {  
    public void run ()   {  
        TimerPopped();
    }  
}

public void TimerPopped () {
    //Do Something
}

ティア、ルーブル

4

1 に答える 1

3

これは問題のように見えます:

private boolean startTimer() {
    // ......
        if (_TimerFuture != null) {
            _TimerFuture.cancel(false);
        }

        _TimerFuture = _Timer.schedule(new TimerPopTask(), 
                                       TIMER_IN_SECONDS, 
                                       TimeUnit.SECONDS);
    // ......
}

キャンセルするために false を渡しているため_TimerFuture、タスクが既に実行されている場合、古いものはキャンセルされない可能性があります。ExecutorServiceとにかく新しいものが作成されます(ただし、スレッドプールサイズが1に固定されているため、同時に実行されません)。いずれにせよ、startTimer() が呼び出されたときにタイマーを再起動するという望ましい動作のようには思えません。

私は少し再設計します。TimerPopTaskインスタンスを「キャンセル」するものにし、インスタンスScheduledFuturesが作成されたらそのままにしておきます。

private class TimerPopTask implements Runnable  {
    //volatile for thread-safety
    private volatile boolean isActive = true;  
    public void run ()   {  
        if (isActive){
            TimerPopped();
        }
    }  
    public void deactivate(){
        isActive = false;
    }
}

次に、のインスタンスではTimerPopTaskなくのインスタンスを保持し、次のようにScheduledFuturestartTimer メソッドを再配置します。

private TimerPopTask timerPopTask;

private boolean startTimer() {
    try {
        if (timerPopTask != null) {
            timerPopTask.deactivate();
        }

        timerPopTask = new TimerPopTask();
        _Timer.schedule(timerPopTask, 
                        TIMER_IN_SECONDS, 
                        TimeUnit.SECONDS);
        return true;
    } catch (Exception e) {
        return false;
    }
}

(stopTimer() メソッドと同様の変更。)

現在のタイマーが期限切れになる前にタイマーを「再起動」する必要があると本当に予想される場合は、スレッドの数を増やすことができます。

private ScheduledExecutorService _Timer = Executors.newScheduledThreadPool(5);

説明したように現在の TimerPopTask と現在の ScheduledFuture の両方への参照を保持し、可能であればそれをキャンセルしてスレッドを解放するために最善を尽くし、キャンセルが保証されていないことを理解して、ハイブリッド アプローチを使用することをお勧めします。

(注: これはすべて、startTimer() および stopTimer() メソッド呼び出しが単一のメイン スレッドに限定され、TimerPopTaskインスタンスのみがスレッド間で共有されることを前提としています。それ以外の場合は、追加の保護が必要になります。)

于 2010-06-23T20:42:20.253 に答える