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

/**
 * 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
    }
}

最佳答案

如前所述,您可以取消提交给计时器的TimerTask而不是取消计时器,这样您就不必新建更多的计时器。

你在做什么:

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

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

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

另外,您不应该使用java.util.Timer。如果您的任何TimerTasks抛出异常会怎样?您的计时器将被杀死,不再运行!如果您的TimerTasks之一变慢或无限期阻塞怎么办?该计时器上的任何其他TimerTasks将无法执行。研究使用ScheduledThreadPoolExecutor代替。我确定下一个Java版本将弃用java.util.Timer。

10-07 19:45
查看更多