Java Timer vs ExecutorService?

263

Eu tenho código onde agendar uma tarefa usando java.util.Timer. Eu estava olhando em volta e vi que ExecutorServicepode fazer o mesmo. Então, nesta pergunta, você usou Timere ExecutorServicepara agendar tarefas, qual é o benefício de um usar em detrimento de outro?

Também queria verificar se alguém havia usado a Timerclasse e se deparou com algum problema que ExecutorServiceeles resolvessem.

kal
fonte
1
E se você precisar de algo ainda mais completo, confira o quartzo . Ele oferece muito mais controle de tarefas, incluindo cron como agendamento, agendamento de reconhecimento de cluster, controle individualizado de tarefas (conceitos como uma execução por vez, dependências etc.). --Tim
Tim

Respostas:

313

De acordo com a concorrência em Java na prática :

  • Timerpode ser sensível a alterações no relógio do sistema, ScheduledThreadPoolExecutornão é.
  • Timerpossui apenas um encadeamento de execução; portanto, tarefas de longa duração podem atrasar outras tarefas. ScheduledThreadPoolExecutorpode ser configurado com qualquer número de threads. Além disso, você tem controle total sobre os threads criados, se desejar (fornecendo ThreadFactory).
  • Exceções de tempo de execução lançadas para TimerTaskmatar esse segmento, tornando Timerinoperantes :-( ... ou seja, as tarefas agendadas não serão mais executadas. ScheduledThreadExecutorNão apenas capturam exceções de tempo de execução, mas permitem lidar com elas se você quiser (substituindo o afterExecutemétodo de ThreadPoolExecutor). Tarefa que A exceção lançada será cancelada, mas outras tarefas continuarão em execução.

Se você pode usar em ScheduledThreadExecutorvez de Timer, faça-o.

Mais uma coisa ... embora ScheduledThreadExecutornão esteja disponível na biblioteca Java 1.4, existe um Backport do JSR 166 ( java.util.concurrent) para o Java 1.2, 1.3, 1.4 , que possui a ScheduledThreadExecutorclasse.

Peter Štibraný
fonte
63

Se estiver disponível, é difícil pensar em um motivo para não usar a estrutura do executor Java 5. A ligar:

ScheduledExecutorService ex = Executors.newSingleThreadScheduledExecutor();

fornecerá uma ScheduledExecutorServicefuncionalidade semelhante a Timer(ou seja, será de thread único), mas cujo acesso pode ser um pouco mais escalável (sob o capô, ele usa estruturas simultâneas em vez de sincronização completa como na Timerclasse). O uso de um ScheduledExecutorServicetambém oferece vantagens como:

  • Você pode personalizá-lo se necessário (consulte newScheduledThreadPoolExecutor()a ScheduledThreadPoolExecutorclasse ou )
  • As execuções 'únicas' podem retornar resultados

Sobre os únicos motivos pelos quais Timerposso me ater são:

  • Está disponível antes do Java 5
  • Uma classe semelhante é fornecida no J2ME, o que poderia facilitar a portabilidade do aplicativo (mas não seria terrivelmente difícil adicionar uma camada comum de abstração nesse caso)
Neil Coffey
fonte
1
Outra razão para o uso TimerTaskpode ser a disponibilidade do scheduledExecutionTime()método que parece não ter nenhum equivalente ScheduledExecutorService.
Rohit Agarwal
3
Outra observação: estou escrevendo este comentário em 2k17, não há mais J2ME. já está morto.
msangel
1
A classe Java Timer é ruim.
JohnyTex
26

ExecutorService é mais recente e mais geral. Um cronômetro é apenas um encadeamento que executa periodicamente as coisas que você agendou para ele.

Um ExecutorService pode ser um pool de encadeamentos ou mesmo se espalhar por outros sistemas em um cluster e fazer coisas como execução em lote única, etc ...

Basta olhar para o que cada um oferece para decidir.

Dustin
fonte
16

Aqui estão mais algumas boas práticas sobre o uso do Timer:

http://tech.puredanger.com/2008/09/22/timer-rules/

Em geral, eu usaria o Timer para coisas rápidas e sujas e o Executor para um uso mais robusto.

Alex Miller
fonte
8

Na página de documentação do Oracle no ScheduledThreadPoolExecutor

Um ThreadPoolExecutor que pode agendar adicionalmente comandos para serem executados após um determinado atraso ou para serem executados periodicamente. Essa classe é preferível ao Timer quando vários threads de trabalho são necessários ou quando a flexibilidade ou os recursos adicionais do ThreadPoolExecutor (que essa classe estende) são necessários.

ExecutorService/ThreadPoolExecutorou ScheduledThreadPoolExecutoré uma escolha óbvia quando você tem vários threads de trabalho.

Prós de ExecutorServicemais deTimer

  1. Timernão pode tirar proveito dos núcleos de CPU disponíveis, ao contrário, ExecutorServiceespecialmente com várias tarefas usando tipos ExecutorServicecomo o ForkJoinPool
  2. ExecutorServicefornece API colaborativa se você precisar de coordenação entre várias tarefas. Suponha que você precise enviar um número N de tarefas do trabalhador e aguardar a conclusão de todas elas. Você pode alcançá-lo facilmente com a API invokeAll . Se você deseja conseguir o mesmo com várias Timertarefas, não seria simples.
  3. O ThreadPoolExecutor fornece uma API melhor para o gerenciamento do ciclo de vida do Thread.

    Os pools de encadeamentos abordam dois problemas diferentes: eles geralmente fornecem desempenho aprimorado ao executar um grande número de tarefas assíncronas, devido à sobrecarga de chamada por tarefa reduzida e fornecem um meio de limitar e gerenciar os recursos, incluindo encadeamentos, consumidos ao executar uma coleção de tarefas. Cada ThreadPoolExecutor também mantém algumas estatísticas básicas, como o número de tarefas concluídas

    Poucas vantagens:

    uma. Você pode criar / gerenciar / controlar o ciclo de vida de Threads e otimizar os custos indiretos de criação de threads

    b. Você pode controlar o processamento de tarefas (roubo de trabalho, ForkJoinPool, invokeAll) etc.

    c. Você pode monitorar o progresso e a integridade dos threads

    d. Fornece melhor mecanismo de tratamento de exceções

Ravindra babu
fonte
5

Meu motivo para às vezes preferir o Timer ao Executors.newSingleThreadScheduledExecutor () é que recebo um código muito mais limpo quando preciso que o timer seja executado nos threads do daemon.

comparar

private final ThreadFactory threadFactory = new ThreadFactory() {
    public Thread newThread(Runnable r) {
        Thread t = new Thread(r);
        t.setDaemon(true);
        return t;
    }
};
private final ScheduledExecutorService timer = Executors.newSingleThreadScheduledExecutor(threadFactory); 

com

private final Timer timer = new Timer(true);

Faço isso quando não preciso da robustez de um serviço executor.

Siamaster
fonte