Таймер - это ранний API в JDK. Обычно мы используем таймер и Timertask для выполнения задач с задержками и периодичностью, прежде чем выйдет NewschedThreadpool, но у таймера есть некоторые недостатки. Почему мы так говорим?
Таймер создает только уникальные потоки для выполнения всех задач таймера. Если выполнение задачи таймера занимает много времени, это вызовет проблемы с точностью других Timertasks. Например, один Timertask выполняется каждые 10 секунд, а другая Timertask выполняется каждые 40 мс. Повторные задачи будут вызоваться 4 раза подряд после выполнения последующих задач, или 4 вызовы полностью «потеряны». Другая проблема с таймером заключается в том, что если Timertask бросит неконтролируемое исключение, это прекратит поток таймера. В этом случае таймер больше не ответит на выполнение потока; Он ошибочно верит, что весь таймер был отменен. Timertask, который был запланирован, но еще не был выполнен, никогда не будет выполнен снова, и новые задачи не могут быть запланированы.
Здесь я сделал небольшую демонстрацию, чтобы воспроизвести проблему, код заключается в следующем:
пакет com.hjc; импорт java.util.timer; import java.util.timertask;/*** Создан Конг в 2018/7/12. */public class timertest {// Создать объект таймера статический таймер timer = new Timer (); public static void main (string [] args) {// добавить задачу 1, timer выполнения задержки. ");}}, 500); // Добавить задачу 2, задержка Timer.schedule (new Timertask () {@Override public void run () {for (;;) {System.out.println ("-Two Task ---"); try {thread.sleep (1000);} catch (retreptexception e) {// todo auto-gate-catcher atecrint e.crint e.frint e.track); }}, 1000); }}Как упоминалось выше, сначала была добавлена задача для выполнения через 500 мс, а затем была добавлена вторая задача для выполнения после 1S. Мы ожидаем, что когда первая задача выведет --- одна задача-и ждет 1s, вторая задача будет выведена-две задачи ---,
Однако после выполнения кода вывод выглядит следующим образом:
Пример 2,
открытый класс Shedule {Private Static Long Start; public static void main (string [] args) {timertask task = new timertask () {public void run () {System.out.println (System.currentTimeMillis ()-start); try {thread.sleep (3000); } catch (прерванное искусство e) {e.printstacktrace (); }}}; Timertask task1 = new timertask () {@override public void run () {system.out.println (System.currentTimeMiLsis ()-start); }}; Таймер timer = new Timer (); start = System.CurrentTimeMillis (); // запустить запланированную задачу, выполнить timer.schedule (задача, 1000); // запустить запланированную задачу, выполнить timer.schedule (task1,3000); }}Мы ожидаем, что вышеупомянутая программа будет выполнена после выполнения первой задачи после выполнения второй задачи 3S, то есть вывод один 1000 и один 3000.
Фактические результаты работы следующие:
Фактические результаты работы не так, как мы хотим. Результатом мира является то, что вторая задача выводится после 4S, то есть 4001 составляет около 4 секунд. Куда ушла эта часть времени? Это время было занято во сне нашей первой задачи.
Теперь мы удаляем Thread.sleep () в первой задаче; Эта строка кода работает правильно? Результаты работы следующие:
Можно видеть, что первая задача выполняется после 1S, а вторая задача выполняется после 3S после выполнения первой задачи.
Это означает, что таймер создает только уникальный поток для выполнения всех задач таймера. Если выполнение задачи таймера занимает много времени, это вызовет проблемы с точностью других Timertasks.
Анализ принципа внедрения таймера
Ниже приведено краткое введение в принцип таймера. Следующий рисунок - введение в принцип модели таймера:
1. TaskQueue - это приоритетная очередь, реализованная сбалансированной бинарной кучей дерева, и каждый объект таймера имеет уникальную очередь задачи. Метод расписания таймера вызова пользователя - добавить задачу Timertask в очередь TaskQueue. При вызове метода расписания параметр длинной задержки используется для указания того, как долго задача задерживается для выполнения.
2. Timerthread - это поток, который выполняет определенную задачу. Он получает задачу с наименьшим приоритетом из очереди TaskQueue для выполнения. Следует отметить, что только после выполнения текущей задачи будет получена следующая задача из очереди. Независимо от того, существует ли установленное время задержки в очереди, таймер имеет только один поток TimerThread, поэтому можно увидеть, что внутренняя реализация таймера является многопродуктивной моделью с одним потребителем.
Из модели реализации мы можем знать, что для изучения вышеуказанной проблемы вам нужно только взглянуть на реализацию Timerthread. Основной логический исходный код метода выполнения Timerthread заключается в следующем:
public void run () {try {mainloop (); } Наконец {// Кто -то убил этот поток, действуя так, как если бы таймер отменил синхронизированный (queue) {newtasksmaybescheduled = false; queue.clear (); // Устранение устаревших ссылок}}} private void mainloop () {while (true) {try {timertask task; логическое задание; // блокировать синхронизированный (queue) {......} if (taskfire) task.run (); // выполнить задачу} catch (прерывание Exception e) {}}}Можно видеть, что, когда исключение, отличное от прерывавшегося, которое будет отменено во время выполнения задачи, единственный потребительский поток будет прекращен из -за того, что бросает исключение, и другие задачи, которые будут выполнены в очереди, будут очищены. Следовательно, лучше всего использовать структуру Try-Catch, чтобы поймать основные возможные исключения в методе прогона Timertask, и не выбросить исключения вне метода прогона.
Фактически, для реализации функций, аналогичных таймеру, это лучший выбор для использования расписания PradeDThreadPoolexeCutor. Одна задача в планировании charedThreadPoolexeCutor бросает исключение, а другие задачи не затронуты.
Пример запланированного количества
/*** Создано Конг 7/7/12. */public class preseedthreadpoolexecutortest {static preseedThreadPoolexeCutor presendThreadPoolexeCutor = new graduLedThreadPoolexeCutor (1); public static void main (string [] args) {wareduldThreadPoolexeCutor.schedule (new Runnable () {public void run () {System.out.println ("-One Task ---"); try {thread.sleep (1000);} chatch ("TreampruptException e) {e.printStacktrace () wrintmeex"). }}, 500, timeUnit.microseconds); graduledThreadPoolexeCutor.schedule (new Runnable () {public void run () {for (int i = 0; i <5; ++ i) {System.out.println ("-Два задачи ---"); try {thread.sleep (1000);} catchtextexcept TimeUnit.microseconds); waredledthreadpoolexecutor.shutdown (); }}Результаты работы следующие:
Причина, по которой задача не влияет на другие задачи, связанные с запланированным набором, заключается в том, что уловка отбрасывает исключение в задаче запланированногофутурет в планировке в плане chreadpoolexecutor, но наилучшая практика для использования Catch, чтобы уловить исключения и печатные журналы в методе запуска задачи пула потоков.