TimerTask 出现IllegalStateException:Task already scheduled or cancelled.

来源:互联网 发布:apt install java 编辑:程序博客网 时间:2024/06/09 20:26

抛出该错误的场景

需求:写一个定时器,定时打开和关闭继电器。可以随时暂停,更改关闭和打开时间出现问题:   定时器的TimerTask只能被shceduler一次,所以当再次调用时,就会抛出该IllegalStateExceptionTask 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();        }
阅读全文
1 0
原创粉丝点击