Antes do Java 1.5, há duas maneiras de criar encadeamentos, um é herdar diretamente o encadeamento e o outro é implementar a interface Runnable. Independentemente do formato usado para implementar o multithreading, precisamos chamar o método start na classe Thread para solicitar io, cup e outros recursos do sistema operacional. Como o método de execução do encadeamento não tem valor de retorno, se você precisar obter o resultado da execução, deverá obter o efeito por meio de variáveis compartilhadas ou pelo uso da comunicação do encadeamento, o que é mais problemático de usar.
Desde o Java 1.5, Callable e Future foram fornecidos, através dos quais você pode obter os resultados da execução da tarefa após a conclusão da tarefa.
Introdução ao Callable e ao Futuro
A interface Callable representa um pedaço de código que pode chamar e retornar resultados; a interface Future representa uma tarefa assíncrona, que é o resultado futuro fornecido por uma tarefa que ainda não foi concluída. Portanto, Callable é usado para produzir resultados e Future é usado para obter resultados.
A interface Callable usa genéricos para definir seu tipo de retorno. A classe Executors fornece alguns métodos úteis para executar tarefas dentro de Callable no pool de threads. Como as tarefas Callable são paralelas (paralelo significa que o todo parece paralelo, de fato, apenas um encadeamento está sendo executado em um determinado momento), precisamos aguardar o resultado que ele retorna. O objeto java.util.concurrent.Future resolve esse problema para nós. Após enviar a tarefa Callable no pool de threads, um objeto Future é retornado.Você pode usá-lo para conhecer o status da tarefa Callable e obter o resultado da execução retornado pelo Callable. Future fornece o método get () para que possamos aguardar o término da chamada e obter seu resultado de execução.
Callable 与 Runnable
java.lang.Runnable, é uma interface e apenas um método run () é declarado nela:
public interface Runnable {
public abstract void run();
}复制代码
Como o valor de retorno do método run () é do tipo nulo, nenhum resultado pode ser retornado após a execução da tarefa.
Callable está localizado no pacote java.util.concurrent.Também é uma interface, e apenas um método é declarado nele, mas esse método é chamado call ():
public interface Callable<V> {
/**
* Computes a result, or throws an exception if unable to do so.
* @return computed result
* @throws Exception if unable to compute a result
*/
V call() throws Exception;
}复制代码
Como você pode ver, essa é uma interface genérica e o tipo retornado pela função call () é o tipo V passado.
Então, como usar o Callable?
Geralmente, é usado em conjunto com o ExecutorService.Várias versões sobrecarregadas do método submit são declaradas na interface ExecutorService:
<T> Future<T> submit(Callable<T> task);
<T> Future<T> submit(Runnable task, T result);
Future<?> submit(Runnable task);复制代码
Futuro
Futuro é cancelar o resultado da execução da tarefa Runnable ou Callable específica, verificar se a consulta foi concluída e obter o resultado. Se necessário, você pode obter o resultado da execução através do método get, que será bloqueado até que a tarefa retorne o resultado.
A classe Future está localizada no pacote java.util.concurrent, que é uma interface:
<T> Future<T> submit(Callable<T> task);
<T> Future<T> submit(Runnable task, T result);
Future<?> submit(Runnable task);复制代码
Cinco métodos são declarados na interface Future. A seguir, é apresentada a função de cada método:
O método cancel é usado para cancelar a tarefa e retorna true se a tarefa for cancelada com êxito e false se a tarefa for cancelada. O parâmetro mayInterruptIfRunning indica se é permitido cancelar a tarefa que está sendo executada mas não concluída.Se definido como true, significa que a tarefa no processo de execução pode ser cancelada. Se a tarefa foi concluída, independentemente de mayInterruptIfRunning ser verdadeiro ou falso, esse método deve retornar false, ou seja, se a tarefa cancelada for cancelada, retornará false; se a tarefa estiver sendo executada, se mayInterruptIfRunning estiver configurado como true, retorne true, se mayInterruptIfRunning estiver definido como false , Ele retorna false; se a tarefa não tiver sido executada, ela retornará true, independentemente de mayInterruptIfRunning ser verdadeiro ou falso.
O método isCancelled indica se a tarefa foi cancelada com êxito e retorna true se a tarefa foi cancelada com sucesso antes que a tarefa fosse concluída normalmente.
O método isDone indica se a tarefa foi concluída; se a tarefa for concluída, ela retornará verdadeiro;
O método get () é usado para obter o resultado da execução, bloqueando e aguardando a conclusão da tarefa antes de retornar;
get (timeout longo, unidade TimeUnit) é usado para obter o resultado da execução.Se o resultado não for obtido dentro do tempo especificado, ele retornará nulo diretamente.
Em outras palavras, o Future fornece três funções:
1) Determine se a tarefa está concluída;
2) capacidade de interromper tarefas;
3) Capaz de obter resultados de execução de tarefas.
Futuro é usado para representar os resultados de cálculos assíncronos. Suas classes de implementação são java.util.concurrent.FutureTask <V> e javax.swing.SwingWorker <T, V>. Se você não deseja que o thread de ramificação bloqueie o thread principal na plataforma Android e deseja obter o resultado da execução do thread de ramificação, use FutureTask .
FutureTask
A classe Java é um design de herança único.Se você implementar multithreading herdando o Thread, não poderá herdar outras classes.O uso de interfaces poderá obter melhor o compartilhamento de dados.
FutureTask implementa a interface RunnableFuture, que é definida da seguinte maneira:
public interface RunnableFuture<V> extends Runnable, Future<V> {
void run();
} 复制代码
Pode-se observar que essa interface implementa as interfaces Runnable e Future, e a implementação concreta na interface é implementada pelo FutureTask. Os dois métodos de construção desta classe são os seguintes:
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
sync = new Sync(callable);
}
public FutureTask(Runnable runnable, V result) {
sync = new Sync(Executors.callable(runnable, result));
}复制代码
Como acima, dois construtores são fornecidos, um com Callable como parâmetro e outro com Runnable como parâmetro. A associação entre essas classes é muito flexível para o método de modelagem de tarefas, permitindo que você escreva a tarefa como Callable com base no recurso Runnable do FutureTask (porque implementa a interface Runnable) e, em seguida, encapsule-a em um agendamento que pode ser cancelado pelo executor quando necessário FutureTask.
FutureTask pode ser agendado pelo executor, o que é crítico. Os métodos que ele fornece são basicamente uma combinação de interfaces Future e Runnable: get (), cancel, isDone (), isCancelled () e run (), e o método run () geralmente é chamado pelo executor, basicamente não o fazemos Ele precisa ser chamado diretamente. A classe FutureTask também implementa a interface Runnable, para que possa ser enviada diretamente ao Thread e Executors para execução:
public class CallableAndFuture {
public static void main(String[] args) {
Callable<Integer> callable = new Callable<Integer>() {
public Integer call() throws Exception {
return new Random().nextInt(100);
}
};
FutureTask<Integer> future = new FutureTask<Integer>(callable);
new Thread(future).start();
try {
Thread.sleep(5000);// 可能做一些事情
int result = future.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
} 复制代码
public class CallableAndFuture {
public static void main(String[] args) {
//ExecutorService.submit()
ExecutorService threadPool = Executors.newSingleThreadExecutor();
Future<Integer> future = threadPool.submit(new Callable<Integer>() {
public Integer call() throws Exception {
return new Random().nextInt(100);
}
});
try {
Thread.sleep(5000);// 可能做一些事情
int result = future.get()); //Future.get()
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
} 复制代码
Se você deseja executar várias tarefas com valores de retorno e obter vários valores de retorno, é possível usar CompletionService :
CompletionService é equivalente a Executor mais BlockingQueue.O cenário de uso é que, quando o thread filho executa simultaneamente uma série de tarefas, o thread principal precisa recuperar o valor de retorno da tarefa do thread filho em tempo real e processar esses valores de retorno sequencialmente. Quem
public class CallableAndFuture {
public static void main(String[] args) {
ExecutorService threadPool = Executors.newCachedThreadPool();
CompletionService<Integer> cs = new ExecutorCompletionService<Integer>(threadPool);
for(int i = 1; i < 5; i++) {
final int taskID = i;
//CompletionService.submit()
cs.submit(new Callable<Integer>() {
public Integer call() throws Exception {
return taskID;
}
});
}
// 可能做一些事情
for(int i = 1; i < 5; i++) {
try {
int result = cs.take().get()); //CompletionService.take()返回Future
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
} 复制代码
Ou não use CompletionService: primeiro crie uma coleção do tipo Futuro, adicione o valor de retorno da tarefa enviada pelo Executor à coleção e, finalmente, facilite a coleta para retirar os dados.
A diferença:
No método de coleta Future, a tarefa de envio não é necessariamente concluída na ordem de adicionar a lista mantida por si mesma. Cada objeto Future que estiver atravessando a lista não está necessariamente em um estado concluído, e o método get () será bloqueado neste momento.Se o sistema for projetado para que cada thread possa continuar a fazer as seguintes ações de acordo com o resultado após a conclusão Isso adicionará tempo de espera adicional aos threads que estão atrás da lista, mas que são concluídos primeiro.
A implementação do CompletionService é manter um BlockingQueue que contém objetos futuros. Somente quando o estado do objeto Futuro terminar, ele será adicionado à Fila.O método take () é realmente o Consumidor no Produtor-Consumidor. Ele pegará o objeto Future da fila e, se a fila estiver vazia, será bloqueada até que um objeto Future concluído seja adicionado à fila.
Portanto, o primeiro concluído deve ser retirado primeiro. Isso reduz o tempo de espera desnecessário.