取消定时器

时间:2009-11-26 03:03:33

标签: java timer

我已经实现了一个令牌系统,它可以分配固定数量的令牌。分配的每个令牌启动一个Timer,该Timer在几分钟后到期并清除该令牌槽以供重用。如果用户在计时器到期之前验证令牌,则应该取消计时器并使用另一个令牌有效期重置。我似乎无法从计时器线程外取消计时器,这是预期的行为。片段如下:

/**
 * Fills one of the available slots with a new session key
 * @param sessionKey
 * @return true on slot fill success - false on fail
 */
public boolean fillSlot(String sessionKey)
{
    if(count<MAXCOUNT)
    {
        //Add key to slot
        slots.add(sessionKey);
        //Up the key count
        upCount();
        //Set up expiry timer
        Timer timer = new Timer();
        timer.schedule(new ExpiringTokentask(timer,sessionKey), EXPIRY_TIME);
        timers.put(sessionKey, timer);
        return true;
    }
    return false;
}

    /**
 * Check if a given key is stored in the slots
 * reset timer every time key is checked
 * @param sessionKey
 * @return true on key found false on not found
 */
public boolean checkSlot(String sessionKey)
{
    //TODO: More efficient key search and storage for larger user sets
    //TODO: Upgrade from memory array to h2 embedded DB
    for(int i=0;i<slots.size();i++)
    {
        if(sessionKey.equals(slots.get(i)))
        {
            //Reset timer
            Timer timer = timers.get(sessionKey);
            //Can't seem to do this
            // timer.cancel();
            timer.schedule(new ExpiringTokentask(timer,sessionKey), EXPIRY_TIME);
            //Return token validation
            return true;
        }
    }

    return false;
}


private class ExpiringTokentask extends TimerTask
{
    private Timer timer;
    private String expireToken;

    public ExpiringTokentask(Timer timer, String sessionKey)
    {
        this.timer = timer;
        this.expireToken = sessionKey;
        System.out.println(sessionKey);
    }

    public void run() {
        System.out.format("Time's up!%n");
        clearSlot(expireToken);
        timer.cancel(); //Terminate the timer thread
    }
}

4 个答案:

答案 0 :(得分:7)

正如所说,你可以取消提交给计时器的TimerTask,而不是取消定时器,这样你就不用再换新的计时器了。

你在做什么:

timer.cancel();
timer.schedule(...);

将抛出IllegalStateExceptions,因为您无法在已取消的计时器上安排新任务。

所以不要这样做:timer.cancel() 使您的映射成为从会话密钥到TimerTasks的映射,并取消TimerTask而不是Timer。这样您就不必新建新的计时器,并且在取消一个或多个任务后,计时器将按预期工作。您还可以使用一个计时器来处理多个会话。现在你正在制作一个Timer,因此每个会话就有一个线程。

另一方面,你不应该使用java.util.Timer。如果任何TimerTasks抛出异常会发生什么?你的计时器将被杀死,永远不会再次运行!如果你的一个TimerTasks很慢或无限期阻塞怎么办?该定时器上的任何其他TimerTasks将无法执行。请考虑使用ScheduledThreadPoolExecutor代替。我确信java.util.Timer将在下一个Java版本中弃用。

答案 1 :(得分:2)

我相信你可以使用一个单一的计时器对象并创建尽可能多的TimerTasks,而不是创建许多计时器。定时器成本很高,因此在整个应用程序中使用一个或两个定时器就足够了。此外,您正在取消定时器,而不是TimerTask尝试取消TimerTask

答案 2 :(得分:1)

如果你的计时器不为空,你应该可以从任何线程调用cancel()。更好的方法是使用ScheduledExecutorService,并为每个提交的任务获取Future。将来,您可以取消它,或检查结果。

答案 3 :(得分:1)

在您尝试取消计时器的行之后,您应该在调用计划方法之前创建一个新计划。

if(sessionKey.equals(slots.get(i)))
{
  //Reset timer
  Timer timer = timers.get(sessionKey);
  //Can't seem to do this
  timer.cancel();
  timer = new Timer();
  timer.schedule(new ExpiringTokentask(timer,sessionKey), EXPIRY_TIME);
  //Return token validation
  return true;
}