Na introdução à programação Java multithread e à criação de threads, apresentamos as maneiras de criar threads, respectivamente herdando Thread, implementando a interface Runnable e implementando a interface Callable.Neste artigo, apresentamos outra maneira de criar threads, FutureTask. Em comparação com a implementação de Runnable, não pode haver nenhum resultado de retorno. FutureTask adapta-o para que possa retornar um resultado padrão e oferece suporte à criação de threads de chamada. Aqui, primeiro usamos um exemplo para apresentar o uso de FutureTask:
private static void futureTaskRunnable() throws InterruptedException, ExecutionException {
//构造方法为一个接口Runnable实现和一个默认的返回值
FutureTask<String> future = new FutureTask<String>(()->{
}, "Hello,Word");
//调用run()方法运行线程
future.run();
System.out.println("futureTaskRunnable: " + future.get());
}
private static void futureTaskcallable() throws InterruptedException, ExecutionException {
//构造方法为接口 Callable的实现
FutureTask<String> future = new FutureTask<String>(()->{
return "test";
});
//调用run()方法运行线程
future.run();
System.out.println("futureTaskcallable: " + future.get());
}
Como no código acima, usamos os métodos Runnable e Callable para criar um FutureTask e podemos chamar o método run () de FutureTask para iniciar o thread. De acordo com o diagrama de hierarquia de classes de FutureTask, FutureTask não apenas implementa a interface Future, mas também implementa a interface Runnable. Portanto, FutureTask pode ser entregue ao Executor para execução.
Anteriormente, dissemos que FutureTask adapta Runnable para que possa retornar um valor padrão. Vejamos como FutureTask se adapta à interface Runnable. Na verdade, este é um modo de adaptador. A seguir está a implementação de FutureTask adaptando interface Runnable.
public FutureTask(Runnable runnable, V result) {
//将Runnable接口封装成Callable接口,方法如下
this.callable = Executors.callable(runnable, result);
this.state = NEW;
}
public static <T> Callable<T> callable(Runnable task, T result) {
if (task == null)
throw new NullPointerException();
//适配器模式,下面我们看RunnableAdapter
return new RunnableAdapter<T>(task, result);
}
//适配类
static final class RunnableAdapter<T> implements Callable<T> {
final Runnable task;
final T result;
RunnableAdapter(Runnable task, T result) {
this.task = task;
this.result = result;
}
public T call() {
task.run();
return result;
}
}
Depois de introduzir o uso de FutureTask, apresentamos o princípio de implementação de FutureTask. No JDK 1.6, Future usa AbstractQueuedSynchronizer. Muitas classes de ferramentas relacionadas à concorrência no pacote JUC usam a classe AQS. Pode-se dizer que é a base das ferramentas de simultaneidade. Se você não conhece esta classe, pode consultar o uso simples do sincronizador de fila AQS AbstractQueuedSynchronizer. AQS é uma estrutura de sincronização que fornece um mecanismo geral para gerenciar atomicamente o estado de sincronização, bloquear e ativar threads e manter a fila de threads bloqueados. Cada sincronizador baseado em AQS conterá dois tipos de operações,
- Pelo menos uma operação de aquisição. Esta operação bloqueia o encadeamento de chamada, a menos que o estado de AQS permita que esse encadeamento continue a execução. A operação de aquisição de FutureTask é chamada de método get () / get (tempo limite longo, unidade TimeUnit).
- Pelo menos uma operação de liberação. Esta operação altera o estado de AQS e o estado alterado permite que um ou mais threads bloqueados sejam desbloqueados. A operação de liberação de FutureTask inclui o método run () e o método cancel (...)
A implementação específica não é apresentada aqui. O uso de AQS é basicamente semelhante. Neste blog, apresentamos principalmente a implementação de Future in Task, que é equivalente à complexidade de usar AQS. A implementação de FutureTask em JDK 1.8 é mais simples e clara. Começamos com o método get (), o código-fonte é o seguinte:
public V get() throws InterruptedException, ExecutionException {
//获取当前FutureTask的状态
int s = state;
//如果未完成,加入到等待队列
if (s <= COMPLETING)
//加入到等待队列,等待线程执行完毕
s = awaitDone(false, 0L);
return report(s);
}
No método get (), se a tarefa não tiver sido executada, ela será adicionada à fila de espera para aguardar a execução da tarefa. Aqui vemos como a fila de espera em FutureTask é diferente daquela em AQS. Comparada com a fila de espera em AQS, em FutureTask Mais simples, sua definição é a seguinte:
static final class WaitNode {
volatile Thread thread;
volatile WaitNode next;
WaitNode() { thread = Thread.currentThread(); }
}
Vamos ver como FutureTask adiciona a thread atual à fila de espera e espera que outra thread retorne antes de continuar. Sua lógica principal está no método awaitDone (), get () e get (long timeout, unidade TimeUnit) são finalmente chamados O código-fonte deste método é o seguinte:
//参数time 是否超时,nanos为毫秒数
private int awaitDone(boolean timed, long nanos) throws InterruptedException {
//如果设置了超时,时间相加,否则为0
final long deadline = timed ? System.nanoTime() + nanos : 0L;
WaitNode q = null;
boolean queued = false;
for (;;) {
//如果线程中断,,从队列移除线程,抛出异常
if (Thread.interrupted()) {
removeWaiter(q);
throw new InterruptedException();
}
int s = state;
//如果已经执行完毕,直接返回
if (s > COMPLETING) {
if (q != null)
q.thread = null;
return s;
}
else if (s == COMPLETING) // 任务正在执行,当前线程变为Ready状态,
Thread.yield();
else if (q == null)
q = new WaitNode(); //创建一个WaitINGNode
else if (!queued)
CAS设置值
queued = UNSAFE.compareAndSwapObject(this, waitersOffset, q.next = waiters, q);
//有超时时间的处理
else if (timed) {
nanos = deadline - System.nanoTime();
if (nanos <= 0L) {
removeWaiter(q);
return state;
}
//调用LockSupport
LockSupport.parkNanos(this, nanos);
}
else
//调用LockSupport
LockSupport.park(this);
}
}
O código acima é o processo de adicionar o encadeamento à fila de espera. O encadeamento retornará para continuar executando o encadeamento atual após a execução da tarefa ser concluída. A seguir, apresentamos a lógica após a tarefa ser executada ou cancelada, principalmente no método run () e no método cancel , Finalmente chama o método finishCompletion (), o código é o seguinte:
private void finishCompletion() {
// assert state > COMPLETING;
for (WaitNode q; (q = waiters) != null;) {
if (UNSAFE.compareAndSwapObject(this, waitersOffset, q, null)) {
for (;;) {
Thread t = q.thread;
if (t != null) {
q.thread = null;
//调用unPart唤醒线程
LockSupport.unpark(t);
}
WaitNode next = q.next;
if (next == null)
break;
q.next = null; // unlink to help gc
q = next;
}
break;
}
}
done();
callable = null; // to reduce footprint
}