TimerTask 出现IllegalStateException:Task already scheduled or cancelled.

抛出该错误的场景

需求:写一个定时器,定时打开和关闭继电器。可以随时暂停,更改关闭和打开时间

出现问题:
   定时器的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();
        }

你可能感兴趣的:(java)