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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2098642/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 03:31:52  来源:igfitidea点击:

Pausing/stopping and starting/resuming Java TimerTask continuously?

javaandroidmultithreadingasynchronoustimer

提问by Faiz

I have one simple question regarding Java TimerTask. How do I pause/resume two TimerTask tasks based on a certain condition? For example I have two timers that run between each other. When a certain condition has been met inside the task of first timer, the first timer stops and starts the second timer, and the same thing happens when a certain condition has been met inside the task of second timer. The class below shows exactly what I mean:

我有一个关于 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();      
    }
}

So my question is, how do I pause timer1while running timer2and vice versa while timer2is running? Performance and timing is my main concern as this needs to be implemented inside another running thread. By the way I am trying to implement these concurrent timers on Android.

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

Thanks for your help!

谢谢你的帮助!

回答by cletus

From TimerTask.cancel():

来自TimerTask.cancel()

Note that calling this method from within the run method of a repeating timer task absolutely guarantees that the timer task will not run again.

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

So once cancelled, it won't ever run again. You'd be better off instead using the more modern ScheduledExecutorService(from Java 5+).

所以一旦取消,它将永远不会再次运行。您最好使用更现代的ScheduledExecutorService(来自 Java 5+)。

Edit:The basic construct is:

编辑:基本结构是:

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

but looking into it there's no way of cancelling that task once its started without shutting down the service, which is a bit odd.

但是查看它,一旦开始该任务就无法取消该任务而不关闭服务,这有点奇怪。

TimerTaskmight be easier in this case but you'll need to create a new instance when you start one up. It can't be reused.

TimerTask在这种情况下可能更容易,但您需要在启动时创建一个新实例。它不能重复使用。

Alternatively you could encapsulate each task as a separate transient service:

或者,您可以将每个任务封装为一个单独的瞬态服务:

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

回答by OscarRyz

If you have already canceled one timer, you can't re-start it, you'll have to create a new one.

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

See this answer, it contains a video and the source code how I did something similar.

看到这个答案,它包含一个视频和源代码我是如何做类似的事情的。

Basically there are two method: pauseand resume

基本上有两种方法:pauseresume

In pause:

暂停中:

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

In resume:

在简历中:

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

That makes the perception of pause/resume.

这使得暂停/恢复的感觉。

If your timers perform different actions based on the state of the application you may consider use the StatePattern

如果您的计时器根据应用程序的状态执行不同的操作,您可以考虑使用StatePattern

Fist define a abstract state:

首先定义一个抽象状态:

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

And provide as many states as you like. The key is that one state leads you to another.

并提供尽可能多的状态。关键是一种状态将您引向另一种状态。

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

And then you change the state in your timer.

然后您更改计时器中的状态。

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

Finally, if what you need is to perform the same thing, but at different rates, you may consider using the TimingFramework. It is a bit more complex but let's you do cool animations, by allowing the painting of certain component take place at different rates ( instead of being linear )

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

回答by OscarRyz

Reviewing your source code, here are the changes ( which pretty much validate my previous answer )

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

In task1:

在任务 1 中:

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

and in task2:

在任务 2 中:

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

It runs on my machine:

它在我的机器上运行:

it works!

有用!

回答by Dan Rosenstark

In my opinion, this is somewhat misguided. If your code needs time guarantees, you can't use Timer anyway, nor would you want to. "This class does not offer real-time guarantees: it schedules tasks using the Object.wait(long) method."

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

The answer, IMHO, is that you don't want to pause and restart your timers. You just want to suppress their run methods from doing their business. And that's easy: you just wrap them in an ifstatement. The switch is on, they run, the switch is off, they miss that cycle.

恕我直言,答案是您不想暂停和重新启动计时器。你只是想抑制他们的运行方法做他们的生意。这很简单:您只需将它们包装在一个if语句中即可。开关打开,它们运行,开关关闭,它们错过了那个循环。

Edit:The question has shifted substantially from what it was originally, but I'll leave this answer in case it helps anyone. My point is: if you don't care when your event fires in the N millisecond span (just that it doesn't EXCEED once every N milliseconds), you can just use conditionals on the run methods. This is, in fact, a very common case, especially when N is less than 1 second.

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

回答by Song Keang

easiest solution i found: just add a boolean in the run code in the timer task, like so:

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

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

回答by Vaibhav Agrawal

I am able to stop a timer and a task using following code:

我可以使用以下代码停止计时器和任务:

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

回答by Stephan Henningsen

Android won't reuse a TimerTask that has already been scheduled once. So it's necessary to reinstantiate both the Timer and TimerTask, for example like this in a Fragment:

Android 不会重用已经安排过一次的 TimerTask。所以有必要重新实例化 Timer 和 TimerTask,例如在 Fragment 中这样:

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.
}

回答by Keshav Gera

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