O timer é uma API inicial no JDK. Geralmente usamos o timer e o TimerTask para realizar tarefas com atrasos e periodicidade antes que o NewscheduledThreadpool seja lançado, mas o timer tem algumas falhas. Por que dizemos isso?
O timer cria apenas threads exclusivos para executar todas as tarefas do timer. Se a execução de uma tarefa de timer for demorada, causará problemas com a precisão de outros Timertosks. Por exemplo, um TimerTask é executado a cada 10 segundos e outro TimerTask é executado a cada 40ms. As tarefas repetidas serão chamadas 4 vezes seguidas após a conclusão das tarefas subsequentes ou 4 chamadas são completamente "perdidas". Outro problema com o timer é que, se o TimerTask lançar uma exceção desmarcada, ele encerrará o thread do timer. Nesse caso, o cronômetro não responderá à execução do thread novamente; Ele acredita erroneamente que todo o cronômetro foi cancelado. O TimerTask, que foi agendado, mas ainda não foi executado, nunca será executado novamente e novas tarefas não podem ser agendadas.
Aqui eu fiz uma pequena demonstração para reproduzir o problema, o código é o seguinte:
pacote com.hjc; importar java.util.timer; importar java.util.timertak;/*** criado por Cong em 2018/7/12. */public class Timertest {// Crie Timer Timer estático Timer = new Timer (); public static void main (string [] args) {// Adicione a Tarefa 1, Timer de Execução de atraso.schedule (new TimerTask () {@Override public void run () {System.out.println ("-One Task ---"); Try {Thread.sLeep (1000);} Catch (One TaskException E); ");}}, 500); // Adicione a tarefa 2, atraso de execução Timer.schedule (new TimerTask () {@Override public void run () {for (;;) {System.out.println ("-dois Task ---"); Try {thread.sleep (1000);} catch (interruptException e) {// para thread. }}, 1000); }}Como mencionado acima, uma tarefa foi adicionada pela primeira vez para executar após 500ms e, em seguida, a segunda tarefa foi adicionada para executar após 1s. O que esperamos é que, quando a primeira tarefa for lançada-uma tarefa-e aguarda 1s, a segunda tarefa será lançada-duas tarefas ---,,,
No entanto, após a execução do código, a saída é a seguinte:
Exemplo 2,
classe pública Shedule {Private Static Long Start; public static void main (string [] args) {theTerTask tarefa = new TimerTask () {public void run () {System.out.println (System.currenttimElis ()-start); tente {thread.sleep (3000); } catch (interruptedException e) {e.printStackTrace (); }}}; TETROTAS DE TIMERTASK1 = new TimerTask () {@Override public void run () {System.out.println (System.currenttimemillis ()-start); }}; Timer timer = new Timer (); start = system.currenttimemillis (); // Inicie uma tarefa agendada, execute timer.schedule (tarefa, 1000); // Inicie uma tarefa programada, execute timer.schedule (tarefa1,3000); }}Esperamos que o programa acima seja executado após a primeira tarefa ser executada após a segunda tarefa ser 3s, ou seja, a saída de 1000 e um 3000.
Os resultados da operação reais são os seguintes:
Os resultados reais da operação não são como desejamos. O resultado do mundo é que a segunda tarefa é emitida após o 4S, ou seja, 4001 é de cerca de 4 segundos. Para onde foi essa parte do tempo? Esse tempo foi ocupado pelo sono de nossa primeira tarefa.
Agora removemos o thread.sleep () na primeira tarefa; Esta linha de código está executando corretamente? Os resultados da operação são os seguintes:
Pode -se observar que a primeira tarefa é executada após 1s e a segunda tarefa é executada após o 3S após a primeira tarefa ser executada.
Isso significa que o timer cria apenas um thread exclusivo para executar todas as tarefas do timer. Se a execução de uma tarefa de timer for demorada, causará problemas com a precisão de outros Timertosks.
Análise de princípio de implementação do timer
A seguir, é apresentada uma breve introdução ao princípio do timer. A figura a seguir é uma introdução ao Modelo Principal de Timer:
1.TaskQueue é uma fila de prioridade implementada pela pilha de árvores binárias equilibradas e cada objeto de timer possui uma fila exclusiva de cena no interior. O método de cronograma do timer de chamada do thread do usuário é adicionar a tarefa TimerTask à fila de tarefa de tarefa. Ao chamar o método do cronograma, o parâmetro de atraso longo é usado para indicar quanto tempo a tarefa é atrasada para ser executada.
2. TimerThread é o thread que executa uma tarefa específica. Ele obtém a tarefa com a menor prioridade da fila de tarefas para execução. Deve -se notar que somente após a execução da tarefa atual a próxima tarefa será obtida na fila. Independentemente de haver um tempo de atraso definido na fila, um timer possui apenas um thread timerthread, para que se possa observar que a implementação interna do timer é um modelo de consumidor único multiprodutor.
No modelo de implementação, podemos saber que, para explorar o problema acima, você só precisa analisar a implementação do TimerThread. O principal código -fonte lógico do método de execução do TimerThread é o seguinte:
public void run () {try {mainloop (); } finalmente {// alguém matou este tópico, agindo como se o timer tivesse cancelado sincronizado (fila) {newTasksmaybescheduled = false; Queue.clear (); // eliminar referências desatualizadas}}} private void mainloop () {while (true) {try {TheTask tarefa; BOOLEAN TASKFURADO; // bloqueio sincronizado (fila) {......} if (taskfired) task.run (); // Executar tarefa} catch (interruptedException e) {}}}Pode -se observar que, quando uma exceção que não seja interrompida é lançada durante a execução de tarefas, o único thread do consumidor será encerrado devido ao lançamento de uma exceção, e outras tarefas a serem executadas na fila serão limpas. Portanto, é melhor usar a estrutura de try-capath para capturar as principais exceções possíveis no método de execução do TimerTask e não lançar as exceções fora do método de execução.
De fato, para implementar funções semelhantes ao Timer, é uma opção melhor usar o cronograma do ScheduledThreadpoolExecutor. Uma tarefa no ScheduledThreadpoolExecutor joga uma exceção e as outras tarefas não são afetadas.
O exemplo do ScheduledThreadpoolExector é o seguinte:
/*** Criado por Cong em 2018/7/12. */public class ScheduledThreadPoolExectestest {Static ScheduledThreadPoolExecutor ScheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor (1); public static void main (string [] args) {scheduledthreadpoolExecutor.schedule (new runnable () {public void run () {System.out.println ("-uma tarefa ---"); tente {thread.sleep (1000);} catch (interruptedException); E.PrintTrack. }, 500, timeunit.microseconds); ScheduledThreadPoolExecutor.schedule (new Runnable () {public void run () {for (int i = 0; i <5; ++ i) {System.out.println ("-duas tarefas {}} {}}} {{thread.sleep (1000);} catch (intercepedException e); Timeunit.microsegunds); scheduledthreadpoolExecutor.shutdown (); }}Os resultados da operação são os seguintes:
A razão pela qual outras tarefas do ScheduledThreadPoolExecutor não são afetadas pela tarefa que lança exceções é porque a captura diminui a exceção na tarefa agendada do FutureTask no ScheduledThreadPoolExecutor, mas é a melhor prática para usar a captura para capturar exceções e imprimir logs no método de execução do tópico do pool.