是否连续暂停/停止和启动/恢复Java TimerTask?

时间:2010-01-20 02:16:11

标签: java android multithreading asynchronous timer

我有一个关于Java TimerTask的简单问题。如何根据特定条件暂停/恢复两个TimerTask任务?例如,我有两个在彼此之间运行的计时器。当在第一计时器的任务内满足某个条件时,第一计时器停止并启动第二计时器,并且当在第二计时器的任务内满足某个条件时发生相同的事情。下面的课程显示了我的意思:

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();      
    }
}

所以我的问题是,在timer1运行时,如何在运行timer2时暂停timer2,反之亦然?性能和时间是我的主要关注点,因为这需要在另一个正在运行的线程中实现。顺便说一句,我试图在Android上实现这些并发计时器。

感谢您的帮助!

8 个答案:

答案 0 :(得分:25)

来自TimerTask.cancel()

  

请注意,从中调用此方法   在重复的运行方法中   计时器任务绝对保证   计时器任务不会再次运行。

因此,一旦取消,它将永远不会再次运行。你最好使用更现代的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);

答案 1 :(得分:15)

我找到的最简单的解决方案:只需在计时器任务的运行代码中添加一个布尔值,如下所示:

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

答案 2 :(得分:6)

如果您已取消一个计时器,则无法重新启动它,您必须创建一个新计时器。

请参阅此answer,它包含一个视频和源代码我是如何做类似的事情。

基本上有两种方法:pauseresume

暂停:

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

简历中:

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

这让人感觉暂停/恢复。

如果您的计时器根据应用程序的状态执行不同的操作,您可以考虑使用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 );

最后,如果您需要的是执行相同的操作,但速度不同,您可以考虑使用TimingFramework。它有点复杂,但让你做一些很酷的动画,允许某些组件的绘画以不同的速率进行(而不是线性)

答案 3 :(得分:4)

查看您的源代码,以下是更改(这几乎验证了我以前的答案)

在task1中:

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

并在任务2中:

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

它在我的机器上运行:

it works! http://img687.imageshack.us/img687/9954/capturadepantalla201001m.png

答案 4 :(得分:4)

在我看来,这有点误导。如果你的代码需要时间保证,你无论如何也不能使用Timer,也不想。 “这个类不提供实时保证:它使用Object.wait(long)方法调度任务。”

答案,恕我直言,你不想暂停和重启你的计时器。你只想抑制他们的运行方法来开展业务。这很容易:你只需将它们包装在if语句中即可。开关打开,它们运行,开关关闭,它们错过了那个循环。

编辑:这个问题已经从最初的问题发生了很大变化,但我会留下这个答案以防万一它能帮到任何人。我的观点是:如果您不关心事件是否在N毫秒范围内触发(只是每N毫秒没有超出一次),您可以在运行方法上使用条件。事实上,这是一种非常常见的情况,特别是当N小于1秒时。

答案 5 :(得分:3)

Android不会重复使用已经安排过一次的TimerTask。因此,有必要重新实现Timer和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.
}

答案 6 :(得分: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;
    }

答案 7 :(得分:0)

    Timer timer1;
    private boolean videoCompleteCDR=false;
    private boolean isVideoPlaying=false;   
    int videoTime=0;
    private int DEFAULT_VIDEO_PLAY_TIME = 30;  

    @Override
    public View onCreate(){
       isVideoPlaying = true; //when server response is successfully
    }


    @Override
    public void onPause() {
        super.onPause();

        if(isVideoPlaying ) {
            if(this.timer1 !=null) {
                this.timer1.cancel();
            }
        }
    }
    @Override
    public void onResume() {
        super.onResume();
        if(isVideoPlaying  && !videoCompleteCDR) {
            callTimerTask();
        }
    }

    @Override
    public void onHiddenChanged(boolean hidden) {
        super.onHiddenChanged(hidden);
        if (!hidden) {
            printLog( "GameFragment  visible ");
            if(isVideoPlaying  && !videoCompleteCDR) {
                callTimerTask();
            }

        } else {
            printLog("GameFragment in visible ");
            if(isVideoPlaying) {
                if(this.timer1 !=null) {
                    this.timer1.cancel();
                }
            }
        }
    }

    private void callTimerTask() {
        // TODO Timer for auto sliding
        printLog( "callTimerTask Start" );
        timer1 = new Timer();
        timer1.schedule(new TimerTask() {
            @Override
            public void run() {
                if (getActivity() != null) {
                    getActivity().runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            if (getActivity() == null) {
                                return;
                            }
                            videoTime++;
                            if(DEFAULT_VIDEO_PLAY_TIME ==videoTime){
                                videoCompleteCDR=true;
                                Log.e("KeshavTimer", "callTimerTask videoCompleteCDR called.... " +videoTime);
                                destroyTimer();
                            }
                            Log.e("KeshavTimer", "callTimerTask videoTime " +videoTime);
                        }
                    });
                } else {
                    printLog("callTimerTask getActivity is null ");
                }
            }
        }, 1000, 1000);
        // TODO  300, 2000;
    }


    private void destroyTimer(){
        this.timer1.cancel();
    }