我已经实现了一个 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。