2010-01-20 200 views
18

我有一个关于Java的TimerTask的一个简单的问题。如何根据特定条件暂停/恢复两个TimerTask任务?例如,我有两个在彼此之间运行的定时器。当某种条件已经满足第一计时器里面的任务,第一计时器停止和启动第二计时器,并在一定的条件已经满足第二定时器里面的任务同样的事情发生。下面的类恰好说明我的意思:暂停/停止并继续启动/恢复Java 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上实现这些并发定时器。

感谢您的帮助!

+0

什么是异常? – cletus 2010-01-20 02:19:44

+2

线程“Timer-1”中的异常java.lang.IllegalStateException:计时器已被取消。 – Faiz 2010-01-20 02:23:20

+0

您可能想考虑转移到'java.util.concurrent'。 '定时器'被认为有点古老。 – 2010-01-20 02:34:15

回答

24

TimerTask.cancel()

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

所以一旦取消,它永远不会再次运行。你会更好,而不是使用更加现代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

你能告诉我如何在上面的类中实现'ScheduledExceutorService'吗?感谢 – Faiz 2010-01-20 02:31:13

+0

这不是关机最好的办法执行者的服务,您可以通过由scheduleAtFixedRate()返回ScheduledFuture取消任务 – axtavt 2010-01-20 02:56:11

+0

@axtavt:'取消()'将停止已经运行 – cletus 2010-01-20 03:21:14

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

+1

https://timingframework.dev.java.net/链接没有工作请重新考虑 – 2012-09-11 09:44:26

4

回顾你的源代码,这里有一些变化以前的答案)

在任务1:

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

它运行在我的机器上:

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

+3

在终端窗口上的漂亮背景:) – 2010-01-21 12:05:52

+0

不错,但如果我想改变两个定时器的时间。可以说定时器1的周期是5000,定时器2是1000,那么我猜这会搞砸整个代码。在那种情况下,我将如何做到这一点 – 2014-03-10 19:10:54

4

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

恕我直言,答案是你不想暂停并重新启动定时器。你只是想压制他们的跑步方法做生意。这很简单:只需将它们包装在if声明中即可。开关打开,他们运行,开关关闭,他们错过了这个周期。

编辑:问题已基本偏离它本来转移,但我会离开这个答案的情况下,它可以帮助任何人。我的观点是:如果你不关心你的事件在N毫秒范围内发生什么(只是它没有超过N毫秒一次),你可以在运行方法上使用条件。事实上,这是一个非常普遍的情况,尤其是当N小于1秒时。

+0

在大多数情况下,这是一个非常可靠和简单的解决方案。 +1 – Alex 2013-03-19 11:27:04

13

最简单的解决办法,我发现:只需添加一个布尔值在定时器任务的运行代码,就像这样:

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

我相信这并不高效。计时器将继续运行并每次检查布尔值。 – Alex 2016-07-09 21:16:28

+0

我怀疑效率是一个值得关注的问题,因为在实践中,这个任务可能会比单个条件操作更费时。这种方法令我担忧的是任务可以在“恢复”后随时启动。这在长时间间隔内可能是显而易见的。 – Jakob 2017-01-28 16:43:34

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

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