Timer & TimerTask versus Thread + sleep em Java

102

Encontrei perguntas semelhantes feitas aqui, mas não havia respostas para minha satisfação. Então, reformulando a pergunta novamente-

Tenho uma tarefa que precisa ser feita periodicamente (digamos, intervalos de 1 minuto). Qual é a vantagem de usar o Timertask & Timer para fazer isso em vez de criar um novo encadeamento que tem um loop infinito com suspensão?

Snippet de código usando timertask-

TimerTask uploadCheckerTimerTask = new TimerTask(){

 public void run() {
  NewUploadServer.getInstance().checkAndUploadFiles();
 }
};

Timer uploadCheckerTimer = new Timer(true);
uploadCheckerTimer.scheduleAtFixedRate(uploadCheckerTimerTask, 0, 60 * 1000);

Snippet de código usando Thread e sleep-

Thread t = new Thread(){
 public void run() {
  while(true) {
   NewUploadServer.getInstance().checkAndUploadFiles();
   Thread.sleep(60 * 1000);
  }
 }
};
t.start();

Eu realmente não preciso me preocupar se perder certos ciclos se a execução da lógica levar mais do que o tempo de intervalo.

Por favor, comente sobre isso ..

Atualização:
Recentemente descobri outra diferença entre usar Timer e Thread.sleep (). Suponha que a hora atual do sistema seja 11h. Se revertermos a hora do sistema para 10:00 AM por algum motivo, o Timer irá PARAR a execução da tarefa até chegar às 11:00, enquanto o método Thread.sleep () continuará executando a tarefa sem obstáculos. Este pode ser um grande tomador de decisões ao decidir o que usar entre os dois.

Keshav
fonte
21
Ponto de ordem: Timer e TimerTask estão obsoletos e foram efetivamente substituídos por ExecutorService, embora seu ponto ainda se mantenha.
skaffman
Obrigado pela dica, decidi usar ExecutorService :)
Keshav
Obrigado a todos pelas respostas, com certeza me deram mais compreensão!
Keshav,
6
O cronômetro não é obsoleto e é preferido quando apenas um único encadeamento é necessário. ( java.sun.com/javase/6/docs/api/java/util/Timer.html )
Justin
2
Timer e TimerTask ainda são úteis em ambientes JME onde ExecutorService não existe (desde JME Java 1.3 baseado ...).
ス ー パ ー フ ァ ミ コ ン

Respostas:

67

A vantagem do TimerTask é que ele expressa sua intenção muito melhor (ou seja, legibilidade do código) e já possui o recurso cancel () implementado.

Observe que ele pode ser escrito em uma forma mais curta, bem como em seu próprio exemplo:

Timer uploadCheckerTimer = new Timer(true);
uploadCheckerTimer.scheduleAtFixedRate(
    new TimerTask() {
      public void run() { NewUploadServer.getInstance().checkAndUploadFiles(); }
    }, 0, 60 * 1000);
Zed
fonte
se o cronômetro for usado para Todos os dias, nos 2 horários específicos 21h e 9h, ... como dar os valores? no código acima ... @Zed?
gumuruh
12

Timer / TimerTask também leva em consideração o tempo de execução de sua tarefa, por isso será um pouco mais preciso. E lida melhor com problemas de multithreading (como evitar deadlocks etc.). E, claro, geralmente é melhor usar um código padrão bem testado em vez de alguma solução caseira.

MartinStettner
fonte
5

Eu não sei por que, mas um programa que eu estava escrevendo estava usando Timers e seu tamanho de heap estava aumentando constantemente, uma vez que eu mudei para Thread / problema de sono resolvido.

Qandeel
fonte
9
O Timer cria uma fila de tarefas que é continuamente atualizada. Quando o cronômetro é concluído, ele não pode ser coletado imediatamente. Portanto, criar mais Timers apenas adiciona mais objetos ao heap. Thread.sleep () apenas pausa a thread, então a sobrecarga de memória seria extremamente baixa.
Darryl Gerrow,
4

Se você thread obtiver exceção e for eliminado, isso é um problema. Mas o TimerTask cuidará disso. Ele será executado independentemente de falha na execução anterior.

Stack Popper
fonte
4

Da Timer documentação :

Java 5.0 introduziu o pacote java.util.concurrent e um dos utilitários de simultaneidade nele é o ScheduledThreadPoolExecutor, que é um pool de threads para executar tarefas repetidamente em uma determinada taxa ou atraso. É efetivamente uma substituição mais versátil para a combinação Timer / TimerTask, pois permite vários threads de serviço, aceita várias unidades de tempo e não requer subclasses de TimerTask (apenas implemente Runnable). Configurar ScheduledThreadPoolExecutor com um thread o torna equivalente a Timer.

Portanto, prefira em ScheduledThreadExecutorvez de Timer:

  • Timerusa um único thread de segundo plano que é usado para executar todas as tarefas do temporizador, sequencialmente. Portanto, as tarefas devem ser concluídas rapidamente, caso contrário, isso atrasará a execução das tarefas subsequentes. Mas no caso de ScheduledThreadPoolExecutorpodemos configurar qualquer número de threads e também podemos ter controle total, fornecendo ThreadFactory.
  • Timerpode ser sensível ao relógio do sistema, pois faz uso do Object.wait(long)método. Mas ScheduledThreadPoolExecutornão é.
  • As exceções de tempo de execução lançadas no TimerTask matarão aquele thread em particular, tornando o Timer morto, pois podemos lidar com isso de ScheduledThreadPoolExecutorforma que as outras tarefas não sejam afetadas.
  • Timerfornece um cancelmétodo para encerrar o cronômetro e descartar todas as tarefas agendadas, no entanto, não interfere na tarefa em execução no momento e permite que ela termine. Mas se o timer estiver rodando como um daemon thread, quer o cancelemos ou não, ele terminará assim que todos os threads do usuário terminarem de ser executados.

Timer vs Thread.sleep

Timer faz uso de Object.waite é diferente deThread.sleep

  1. Um waitthread em espera ( ) pode ser notificado (usandonotify ) por outro thread, mas um thread em não pode ser, ele só pode ser interrompido.
  2. Uma espera (e notificação) deve acontecer em um bloco sincronizado no objeto de monitor, enquanto o sono não.
  3. Enquanto hibernar não libera o bloqueio, a espera irá liberar o bloqueio para o objeto esperar é chamado.
akhil_mittal
fonte
informações muito úteis, além de usar Thread.sleep em um loop infinito pode causar alto uso da CPU em pequenos atrasos.
Amir Fo
3

Há um argumento crucial contra o gerenciamento dessa tarefa usando sleepmétodos e threads de Java . Você está usando while(true)para permanecer indefinidamente no loop e hibernar o thread colocando-o no modo de espera. E se NewUploadServer.getInstance().checkAndUploadFiles();ocupar alguns recursos sincronizados. Outros threads não conseguirão acessar esses recursos. A inanição pode ocorrer, o que pode tornar todo o seu aplicativo lento. Esses tipos de erros são difíceis de diagnosticar e é uma boa ideia prevenir sua existência.

A outra abordagem dispara a execução do código que importa para você, ou seja NewUploadServer.getInstance().checkAndUploadFiles();, chamando o run()método do seu TimerTaskenquanto permite que outras threads usem os recursos.

Boris Pavlović
fonte
16
Eu não entendo esse argumento. Ambas as opções iniciam o mesmo método a partir de um encadeamento, ambas as opções ficam suspensas em um encadeamento enquanto aguardam a execução. Não haverá diferenças de fome entre as duas opções.
sábado
2

Acho que entendo seu problema, estou vendo algo muito semelhante. Tenho temporizadores que são recorrentes, alguns a cada 30 minutos e alguns a cada dois dias. Pelo que li e pelos comentários que vejo, parece que a coleta de lixo nunca será executada porque todas as tarefas nunca são concluídas. Eu pensaria que a coleta de lixo seria executada quando um cronômetro está em hibernação, mas não estou vendo e de acordo com a documentação, não.

Eu acho que a geração de novos threads conclui e permite a coleta de lixo.

Alguém, por favor, prove que estou errado, reescrever o que herdei vai ser uma dor.

Ken
fonte