CodeGym /Cursos /All lectures for PT purposes /ExecutorService interface

ExecutorService interface

All lectures for PT purposes
Nível 1, Lição 65
Disponível

Por que você precisa da interface do Executor?

Antes do Java 5, você tinha que escrever todo o seu próprio gerenciamento de encadeamento de código em seu aplicativo. Além disso, criar umnovo tópicoO objeto é uma operação com uso intensivo de recursos e não faz sentido criar um novo thread para cada tarefa leve. E como esse problema é familiar para absolutamente todos os desenvolvedores de aplicativos multiencadeados, eles decidiram trazer essa funcionalidade para o Java como a estrutura Executor .

Qual é a grande idéia? É simples: ao invés de criar uma nova thread para cada nova tarefa, as threads são mantidas em uma espécie de "armazenamento", e quando chega uma nova tarefa, recuperamos uma thread existente ao invés de criar uma nova.

As principais interfaces dessa estrutura são Executor , ExecutorService e ScheduledExecutorService , cada uma das quais estende a funcionalidade da anterior.

A interface Executor é a interface base. Ele declara um único método void execute(Runnable command) que é implementado por um objeto Runnable .

A interface ExecutorService é mais interessante. Possui métodos para gerenciar a conclusão do trabalho, bem como métodos para retornar algum tipo de resultado. Vamos dar uma olhada em seus métodos:

Método Descrição
void desligamento(); Chamar esse método interrompe o ExecutorService . Todas as tarefas que já foram enviadas para processamento serão concluídas, mas novas tarefas não serão aceitas.
List<Executável> shutdownNow();

Chamar esse método interrompe o ExecutorService . Thread.interrupt será chamado para todas as tarefas que já foram enviadas para processamento. Este método retorna uma lista de tarefas enfileiradas.

O método não espera a conclusão de todas as tarefas que estão "em andamento" no momento em que o método é chamado.

Aviso: chamar esse método pode vazar recursos.

booleano isShutdown(); Verifica se o ExecutorService está parado.
boolean isTerminated(); Retorna true se todas as tarefas foram concluídas após o desligamento do ExecutorService . Até que shutdown() ou shutdownNow() seja chamado, ele sempre retornará false .
boolean awaitTermination (long timeout, TimeUnit unit) gera InterruptedException;

Depois que o método shutdown() é chamado, esse método bloqueia o thread no qual é chamado, até que uma das seguintes condições seja verdadeira:

  • todas as tarefas agendadas estão concluídas;
  • o timeout passado para o método expirou;
  • o segmento atual é interrompido.

Retorna verdadeiro se todas as tarefas forem concluídas e falso se o tempo limite expirar antes do término.

<T> Future<T> submit(Tarefa Callable<T>);

Adiciona uma tarefa Callable ao ExecutorService e retorna um objeto que implementa a interface Future .

<T> é o tipo do resultado da tarefa passada.

<T> Future<T> submit(Tarefa Executável, T resultado);

Adiciona uma tarefa Runnable ao ExecutorService e retorna um objeto que implementa a interface Future .

O parâmetro de resultado T é o que é retornado por uma chamada para o método get() no resultadoObjeto futuro.

Future<?> submit(tarefa executável);

Adiciona uma tarefa Runnable ao ExecutorService e retorna um objeto que implementa a interface Future .

Se chamarmos o método get() no objeto Future resultante , obteremos null.

<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) lança InterruptedException;

Passa uma lista de tarefas Callable para o ExecutorService . Retorna uma lista de Futuros da qual podemos obter o resultado do trabalho. Essa lista é retornada quando todas as tarefas enviadas são concluídas.

Se a coleção de tarefas for modificada durante a execução do método, o resultado desse método será indefinido.

<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException;

Passa uma lista de tarefas Callable para o ExecutorService . Retorna uma lista de Futuros da qual podemos obter o resultado do trabalho. Essa lista é retornada quando todas as tarefas passadas são concluídas ou após o tempo limite passado para o método ter decorrido, o que ocorrer primeiro.

Se o tempo limite expirar, as tarefas inacabadas serão canceladas.

Observação: é possível que uma tarefa cancelada não pare de ser executada (veremos esse efeito colateral no exemplo).

Se a coleção de tarefas for modificada durante a execução do método, o resultado desse método será indefinido.

<T> T invokeAny(Collection<? extends Callable<T>> tasks) lança InterruptedException, ExecutionException;

Passa uma lista de tarefas Callable para o ExecutorService . Retorna o resultado de uma das tarefas (se houver) concluída sem gerar uma exceção (se houver).

Se a coleção de tarefas for modificada durante a execução do método, o resultado desse método será indefinido.

<T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) lança InterruptedException, ExecutionException, TimeoutException;

Passa uma lista de tarefas Callable para o ExecutorService . Retorna o resultado de uma das tarefas (se houver) concluída sem lançar uma exceção antes que o tempo limite passado para o método tenha decorrido.

Se a coleção de tarefas for modificada durante a execução do método, o resultado desse método será indefinido.

Vejamos um pequeno exemplo de trabalho com ExecutorService .


import java.util.List;
import java.util.concurrent.*;

public class ExecutorServiceTest {
   public static void main(String[] args) throws InterruptedException, ExecutionException, TimeoutException {
//Create an ExecutorService for 2 threads
       java.util.concurrent.ExecutorService executorService = new ThreadPoolExecutor(2, 2, 60, TimeUnit.SECONDS, new ArrayBlockingQueue<>(10));
// Create 5 tasks
       MyRunnable task1 = new MyRunnable();
       MyRunnable task2 = new MyRunnable();
       MyRunnable task3 = new MyRunnable();
       MyRunnable task4 = new MyRunnable();
       MyRunnable task5 = new MyRunnable();

       final List<MyRunnable> tasks = List.of(task1, task2, task3, task4, task5);
// Pass a list that contains the 5 tasks we created
       final List<Future<Void>> futures = executorService.invokeAll(tasks, 6, TimeUnit.SECONDS);
       System.out.println("Futures received");

// Stop the ExecutorService
       executorService.shutdown();

       try {
           TimeUnit.SECONDS.sleep(3);
       } catch (InterruptedException e) {
           e.printStackTrace();
       }

       System.out.println(executorService.isShutdown());
       System.out.println(executorService.isTerminated());
   }

   public static class MyRunnable implements Callable<Void> {

       @Override
       public void call() {
// Add 2 delays. When the ExecutorService is stopped, we will see which delay is in progress when the attempt is made to stop execution of the task
           try {
               TimeUnit.SECONDS.sleep(3);
           } catch (InterruptedException e) {
               System.out.println("sleep 1: " + e.getMessage());
           }
           try {
               TimeUnit.SECONDS.sleep(2);
           } catch (InterruptedException e) {
               System.out.println("sleep 2: " + e.getMessage());
           }
           System.out.println("done");
           return null;
       }
   }
}

Saída:

feito
feito
Futuros recebidos
sono 1: sono interrompido
sono 1: sono interrompido
feito
feito
verdadeiro
verdadeiro

Cada tarefa é executada por 5 segundos. Criamos um pool para dois threads, então as duas primeiras linhas de saída fazem todo o sentido.

Seis segundos após o início do programa, o método invokeAll atinge o tempo limite e o resultado é retornado como uma lista de Futures . Isso pode ser visto na string de saída Futuros recebidos .

Depois que as duas primeiras tarefas são concluídas, outras duas começam. Mas como o tempo limite definido no método invokeAll expira, essas duas tarefas não têm tempo para serem concluídas. Eles recebem um comando "cancelar" . É por isso que a saída tem duas linhas com sono 1: sono interrompido .

E então você pode ver mais duas linhas com done . Este é o efeito colateral que mencionei ao descrever o método invokeAll .

A quinta e última tarefa nunca é iniciada, então não vemos nada sobre ela na saída.

As duas últimas linhas são o resultado da chamada dos métodos isShutdown e isTerminated .

Também é interessante executar este exemplo no modo de depuração e observar o status da tarefa após o tempo limite (defina um ponto de interrupção na linha com executorService.shutdown(); ):

Vemos que duas tarefas foram concluídas normalmente e três tarefas foram "canceladas" .

ScheduledExecutorService

Para concluir nossa discussão sobre executores, vamos dar uma olhada em ScheduledExecutorService .

Possui 4 métodos:

Método Descrição
public ScheduledFuture<?> schedule(comando executável, longo atraso, unidade TimeUnit); Agenda a tarefa Runnable aprovada para ser executada uma vez após o atraso especificado como um argumento.
public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit); Agenda a tarefa Callable aprovada para ser executada uma vez após o atraso especificado como um argumento.
public ScheduledFuture<?> scheduleAtFixedRate(comando executável, atraso inicial longo, período longo, unidade TimeUnit); Agenda a execução periódica da tarefa aprovada, que será executada pela primeira vez após initialDelay , e cada execução subsequente começará após period .
public ScheduledFuture<?> scheduleWithFixedDelay(comando executável, atraso inicial longo, atraso longo, unidade TimeUnit); Agenda a execução periódica da tarefa aprovada, que será executada pela primeira vez após initialDelay , e cada execução subsequente começará após o atraso (o período entre a conclusão da execução anterior e o início da atual).
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION