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

Posted

技术标签:

【中文标题】连续暂停/停止和启动/恢复Java TimerTask?【英文标题】:Pausing/stopping and starting/resuming Java TimerTask continuously? 【发布时间】:2011-01-07 02:31:22 【问题描述】:

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

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

感谢您的帮助!

【问题讨论】:

线程“Timer-1”java.lang.IllegalStateException 中的异常:定时器已取消。 您可能想考虑转到java.util.concurrentTimer 被认为有点老派。 我从停止/重新启动到暂停/恢复计时器进行了编辑。是否可以使用Thread.sleep(milliseconds) 我意识到这只是示例代码,而您正在做更大的事情。但是在这个例子中,你不会使用两个计时器,而是一个,带有适当的 if 条件来指示在 run 方法中执行什么行为。很多时候,人们在许多计时器和许多 TimerTasks 中思考,当你只需要它时,如果你有某种需要它的周期性(一个是 20 毫秒,另一个是每 2 分钟)。即使在那种情况下,我也不会停下来,正如我在回答中所说的那样。我只想改变 run 方法的作用。当然,除非他们正在做一些对时间敏感的事情(例如,运行计时码表)。 Tom Hawtin,Timer old school 怎么样?我见过人们使用基于线程的概念来解决可以通过计时器轻松解决的实时问题。定时器活得很好,线程非常适合线程。 【参考方案1】:

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

【讨论】:

你能告诉我如何在上面的类中实现ScheduledExceutorService吗?谢谢 关闭执行器服务不是最好的办法,可以通过 scheduleAtFixedRate() 返回的 ScheduledFuture 取消任务【参考方案2】:

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

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

【讨论】:

我相信这不是有效的。计时器将继续运行并每次检查布尔值。 我怀疑效率是一个问题,因为在实践中,任务中的操作可能比单个条件更耗时。这种方法让我担心的是,任务可以在“恢复”后随时触发。这在很长一段时间内可能会很明显。 这可能达到目的,但对烦人的解决方案很友好,如果我们可以跟踪这个过程,并且按需暂停和恢复会很好【参考方案3】:

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

看到这个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。它有点复杂,但让我们做很酷的动画,通过允许某些组件的绘制以不同的速率(而不是线性)进行

【讨论】:

【参考方案4】:

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

恕我直言,答案是您不想暂停并重新启动计时器。您只想阻止他们的运行方法开展业务。这很简单:您只需将它们包装在if 语句中。开关打开,它们运行,开关关闭,它们错过了那个循环。

编辑:这个问题与原来的问题有很大的不同,但我会留下这个答案,以防它对任何人有帮助。我的观点是:如果您不在乎您的事件何时在 N 毫秒范围内触发(只是它不会每 N 毫秒超过一次),您可以在运行方法上使用条件。事实上,这是一种非常常见的情况,尤其是当 N 小于 1 秒时。

【讨论】:

在大多数情况下,这是一个非常可靠和简单的解决方案。 +1【参考方案5】:

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

在任务1中:

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

它在我的机器上运行:

【讨论】:

终端窗口的背景很好:) 很好,但是如果我想改变两个定时器的周期怎么办。假设定时器 1 的周期是 5000,定时器 2 是 1000,那么我想这会搞砸整个代码。在那种情况下,我怎么能做到这一点【参考方案6】:

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.

【讨论】:

【参考方案7】:

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

    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;
    

【讨论】:

【参考方案8】:
    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();
    

【讨论】:

以上是关于连续暂停/停止和启动/恢复Java TimerTask?的主要内容,如果未能解决你的问题,请参考以下文章

如何暂停/恢复 aws lambda 函数

在谷歌计算引擎上启动和停止实例

线程的停止和暂停

Java ExecutorService 暂停/恢复特定线程

如何在 Inno Setup 中制作停止和暂停/恢复/播放音乐按钮

iOS7 CLLocationManager 暂停更新并且不会恢复