FAQ: Threads

 
 
 
Сообщения:9474
Содержание
Проблемы java.util.Timer'a
Изменен:19 сен 2011 10:20
 
 
Сообщения:9474
Problems of java.util.Timer

Итак, в стандартных классах JavaSE есть несколько способов выполнять задачи по графику или с периодичностью. Здесь будет описано почему лучше не использовать для этого java.util.Timer и какие есть альтернативы. Данная статья вдохновлена книгой Java Concurrency In Practice.
Итак, во-первых Timer выполняет все задачи в одном потоке. Это может стать проблемой например, при выполнении такого кода:
timer.scheduleAtFixedRate(task, 0, 100);
Вроде на первый взгляд проблем нет - задача выполняется каждые 100 миллисекунд, однако что если сама задача выполняется 3 секунды? Ведь поток-то у нас один. Этот пример демонстрирует, что задача будет выполняться раз в три секунды, а не раз в 100 миллисекунд, то бишь все, что Timer сделать не успел, он продолжает собирать и таким образом задача уже может никогда и не выполнится (если она была зашедулена на время, которое уже прошло) :
public class TooLongTime {
    public static void main(String[] args) throws InterruptedException {
        Timer timer = new Timer();
        timer.scheduleAtFixedRate(new TooLongTask(), 0, 100);
        Thread.sleep(10 * 1000);
    }

    public static class TooLongTask extends TimerTask {
        @Override
        public void run() {
            try {
                System.out.println("Say something");
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
Если бы мы использовали ScheduledThreadPoolExecutor, то он бы позволил задать количество потоков, которые разбирают задачи и проблемы у нас бы такой не возникло.
Следующая проблема с Timer - это то, что если задача выбросила исключение, то Timer ведет себя будто его просто отключили. Демонстрация:
public class OutOfTime {
    public static void main(String[] args) throws InterruptedException {
        Timer timer = new Timer();
        timer.schedule(new ThrowTask(), 1);
        Thread.sleep(1000);
        timer.schedule(new ThrowTask(), 1);
    }
    public static class ThrowTask extends TimerTask {
        @Override
        public void run() {
            throw new UnsupportedOperationException();
        }
    }
}
В итоге мы увидим залогированное UnsupportedOperationException, затем мы увидим, что таймер пытался снова запустить задачу, но выведет он: Exception in thread "main" java.lang.IllegalStateException: Timer already cancelled. Ну и конечно же задачи, которые следующие на очереди по времени, тоже не будут никогда больше выполнены. ScheduledThreadPoolExecutor в такой ситуации как минимум создал бы новый поток, и следующая задача, как и просто запланированные уже задачи, выполнялись бы дальше.
Еще недостаток: TimerTask - это класс, а не интерфейс, что выливается как всегда в наследование.
Дальше: Timer использует абсолютное время (System.currentMillis()), что значит, что могут возникнут проблемы если вы переведете системные часы. ScheduledThreadPoolExecutor использует относительное время (относительно своего старта) и проблем здесь быть не может.
Кроме ScheduledThreadPoolExecutor вы также можете взглянуть на DelayQueue - это очередь, которая отдает свои элементы только когда указанное время истекло, из нее можно забирать эти элементы и выполнять сразу же.
 
 
Сообщения:2030
Еще в Timer'е в свое время была обнаружена ошибка. Если запустить Таймер и перевести часы, то он ведет себя непредсказуемо.

Всякое решение плодит новые проблемы
 
Модераторы:Нет
Сейчас эту тему просматривают:Нет