我已经实现了一个令牌系统,它可以分配固定数量的令牌。分配的每个令牌启动一个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
}
}
答案 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;
}