抛出该错误的场景
需求:写一个定时器,定时打开和关闭继电器。可以随时暂停,更改关闭和打开时间
出现问题:
定时器的TimerTask只能被shceduler一次,所以当再次调用时,就会抛出该IllegalStateException:Task already scheduled or cancelled.
查找问题
debug查看代码后发现,task有个标识符state
/**
* The state of this task, chosen from the constants below.
*/
int state = VIRGIN;
/**
* This task has not yet been scheduled.
*/
static final int VIRGIN = 0;
/**
* This task is scheduled for execution. If it is a non-repeating task,
* it has not yet been executed.
*/
static final int SCHEDULED = 1;
/**
* This non-repeating task has already executed (or is currently
* executing) and has not been cancelled.
*/
static final int EXECUTED = 2;
/**
* This task has been cancelled (with a call to TimerTask.cancel).
*/
static final int CANCELLED = 3;
当实例被调度,之后状态就会改变,当状态不等于VIRGIN 时,就会抛出错误,看源码
private void sched(TimerTask task, long time, long period) {
if (time < 0)
throw new IllegalArgumentException("Illegal execution time.");
// Constrain value of period sufficiently to prevent numeric
// overflow while still being effectively infinitely large.
if (Math.abs(period) > (Long.MAX_VALUE >> 1))
period >>= 1;
synchronized(queue) {
if (!thread.newTasksMayBeScheduled)
throw new IllegalStateException("Timer already cancelled.");
synchronized(task.lock) {
if (task.state != TimerTask.VIRGIN) //当state不等于0时,就会跑
throw new IllegalStateException(
"Task already scheduled or cancelled");
task.nextExecutionTime = time;
task.period = period;
task.state = TimerTask.SCHEDULED;
}
queue.add(task);
if (queue.getMin() == task)
queue.notify();
}
}
解决问题
1、通过改变标识符state来达到目的,但state是默认的,访问权限只有包和同个类,所以我们通过反射来设置
timer = new Timer();
task = RelayOpenTask.getIntance();
//task.setMap(uuidMap, this);
Field field;
try {
field = TimerTask.class.getDeclaredField("state");
field.setAccessible(true);
field.set(task, 0);
} catch (NoSuchFieldException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}