18


7

Java TimerTaskを継続的に一時停止/停止および開始/再開しますか?

Java TimerTaskに関する簡単な質問が1つあります。 特定の条件に基づいて2つのTimerTaskタスクを一時停止/再開するにはどうすればよいですか? たとえば、相互に実行する2つのタイマーがあります。 最初のタイマーのタスク内で特定の条件が満たされると、最初のタイマーは2番目のタイマーを停止および開始し、2番目のタイマーのタスク内で特定の条件が満たされると同じことが起こります。 以下のクラスは、私が意味することを正確に示しています。

public class TimerTest {
    Timer timer1;
    Timer timer2;
    volatile boolean a = false;

    public TimerTest() {
        timer1 = new Timer();
        timer2 = new Timer();
    }

    public void runStart() {
        timer1.scheduleAtFixedRate(new Task1(), 0, 1000);
    }

    class Task1 extends TimerTask {
        public void run() {
            System.out.println("Checking a");
            a = SomeClass.getSomeStaticValue();

            if (a) {
                // Pause/stop timer1, start/resume timer2 for 5 seconds
                timer2.schedule(new Task2(), 5000);
            }
        }
    }

    class Task2 extends TimerTask{
        public void run() {
            System.out.println("Checking a");
            a = SomeClass.getSomeStaticValue();

            if (!a) {
                // Pause/stop timer2, back to timer1
                timer1.scheduleAtFixedRate(new Task1(), 0, 1000);
            }

            // Do something...
        }
    }

    public static void main(String args[]) {
        TimerTest tt = new TimerTest();
        tt.runStart();
    }
}

私の質問は、「timer2」の実行中に「timer1」を一時停止し、「timer2」の実行中にその逆を行うにはどうすればよいですか? パフォーマンスとタイミングは、実行中の別のスレッド内に実装する必要があるため、私の主な関心事です。 ところで、私はこれらの並行タイマーをAndroidに実装しようとしています。

ご協力いただきありがとうございます!

7 Answer


25


_ 繰り返しタイマータスクのrunメソッド内からこのメソッドを呼び出すと、タイマータスクが再度実行されないことが絶対に保証されます。 _

そのため、キャンセルすると、再び実行されることはありません。 代わりに、より最新のhttp://java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/ScheduledExecutorService.html [ScheduledExecutorService](Java 5+ )。

*編集:*基本的な構成は次のとおりです。

ScheduledExecutorService exec = Executors.newSingleThreadScheduledExecutor();
exec.scheduleAtFixedRate(runnable, 0, 1000, TimeUnit.MILLISECONDS);

ただし、サービスを停止せずに開始したタスクをキャンセルする方法はありません。これは少し奇妙です。

この場合、 `TimerTask`は簡単かもしれませんが、起動時に新しいインスタンスを作成する必要があります。 再利用できません。

または、各タスクを個別の一時サービスとしてカプセル化することもできます。

final ScheduledExecutorService exec = Executors.newSingleThreadScheduledExecutor();
Runnable task1 = new Runnable() {
  public void run() {
    a++;
    if (a == 3) {
      exec.shutdown();
      exec = Executors.newSingleThreadScheduledExecutor();
      exec.scheduleAtFixedRate(task2, 0, 1000, TimeUnit.MILLISECONDS)
    }
  }
};
exec.scheduleAtFixedRate(task1, 0, 1000, TimeUnit.MILLISECONDS);


15


私が見つけた最も簡単な解決策:タイマータスクの実行コードにブール値を追加するだけです:

timer.schedule( new TimerTask() {
    public void run() {
       if(!paused){
           //do your thing
       }
    }
 }, 0, 1000 );


6


既に1つのタイマーをキャンセルしている場合、再起動することはできません。新しいタイマーを作成する必要があります。

このhttps://stackoverflow.com/questions/2096578/java-replacement-for-infinite-loops/2097100#2097100[answer]を参照してください。これには、ビデオとソースコードが含まれており、どのように似たようなことをしましたか。

基本的には、「一時停止」と「再開」の2つの方法があります。

一時停止中:

public void pause() {
    this.timer.cancel();
}

再開中:

public void resume() {
    this.timer = new Timer();
    this.timer.schedule( aTask, 0, 1000 );
}

それは一時停止/再開の知覚になります。

タイマーがアプリケーションの状態に基づいて異なるアクションを実行する場合、http://en.wikipedia.org/wiki/State_pattern [StatePattern]を使用することを検討できます。

拳は抽象的な状態を定義します:

abstract class TaskState  {
    public void run();
    public TaskState next();
}

そして、あなたが好きなだけ多くの状態を提供します。 重要なのは、ある状態から別の状態に導くことです。

class InitialState extends TaskState {
    public void run() {
        System.out.println( "starting...");
    }
    public TaskState next() {
         return new FinalState();
    }
 }
 class FinalState extends TaskState  {
     public void run() {
         System.out.println("Finishing...");
     }
     public TaskState next(){
         return new InitialState();
    }
 }

そして、タイマーの状態を変更します。

Timer timer = new Timer();
TaskState state = new InitialState();

timer.schedule( new TimerTask() {
     public void run() {
          this.state.run();
          if( shouldChangeState() ) {
              this.state = this.state.next();
           }
     }
 }, 0, 1000 );

最後に、同じことを異なるレートで実行する必要がある場合は、https://timingframework.dev.java.net/ [TimingFramework]の使用を検討できます。 少し複雑ですが、特定のコンポーネントのペイントを異なるレートで(線形ではなく)実行できるようにすることで、クールなアニメーションを作成できます。


4


ソースコードを確認して、変更点を示します(以前の回答をほぼ検証します)。

task1で:

// Stop timer1 and start timer2
timer1.cancel();
timer2 = new Timer(); // <-- just insert this line
timer2.scheduleAtFixedRate(new Task2(), 0, 1000);

そしてtask2で:

// Stop timer2 and start timer1
timer2.cancel();
timer1 = new Timer(); // <-- just insert this other
timer1.scheduleAtFixedRate(new Task1(), 0, 1000);

私のマシンで実行されます:


4


私の意見では、これはやや見当違いです。 コードに時間の保証が必要な場合、とにかくTimerを使用することもできません。 「このクラスはリアルタイムの保証を提供しません。Object.wait(long)メソッドを使用してタスクをスケジュールします。」

私見、答えはタイマーを一時停止したり再起動したくないということです。 実行メソッドがビジネスを実行しないようにしたいだけです。 そしてそれは簡単です:あなたはそれらを `if`ステートメントでラップするだけです。 スイッチがオンになり、実行され、スイッチがオフになり、そのサイクルを逃します。

*編集:*質問は元の質問から大幅にシフトしましたが、誰かに役立つ場合に備えてこの回答を残します。 私のポイントは、イベントがNミリ秒のスパンで発生することを気にしない場合(Nミリ秒に1回は超えないということだけ)、runメソッドで条件を使用するだけです。 これは実際、特にNが1秒未満の場合に非常に一般的なケースです。


3


Androidは、一度スケジュールされたTimerTaskを再利用しません。 そのため、タイマーとTimerTaskの両方を再インスタンス化する必要があります。たとえば、次のようなフラグメントです。

private Timer timer;
private TimerTask timerTask;

public void onResume ()
{
    super.onResume();

    timer = new Timer();
    timerTask = new MyTimerTask();
    timer.schedule(timerTask, 0, 1000);
}

public void onPause ()
{
    super.onPause();
    timer.cancel(); // Renders Timer unusable for further schedule() calls.
}


0


次のコードを使用して、タイマーとタスクを停止できます。

    if(null != timer)
    {

        timer.cancel();
        Log.i(LOG_TAG,"Number of cancelled tasks purged: " + timer.purge());
        timer = null;
    }

    if(task != null)
    {
        Log.i(LOG_TAG,"Tracking cancellation status: " + task.cancel());
        task = null;
    }