有这样一种场景,用多线程发送数据到某个服务器,须要知道各个线程是否都发送成功,等全部线程都发送完成才能继续下一轮计算和发送。若是用传统的多线程方式,就须要启动多个线程,而后在每一个线程中分别发送数据,外部经过某种方式等待各个线程所有都发送完成,再进行后面的计算等流程。这种实现方式的代码会比较臃肿,在java中提供了一种Callable+Future的方法,能够将异步的多线程调用变为同步方式。java
Callable编程
在java的多线程编程中,有Thread和Runnable两种方式来新建线程,其中Runnable封装了一个异步运行的任务,能够认为是一个没有任何参数和返回值的异步方法。Callable接口相似于Runnable,二者都是为那些其实例可能被另外一个线程执行的类设计的,不一样之处在于:服务器
Runnable不会返回结果,而且没法抛出通过检查的异常。而Callable是有返回结果而且可能抛出异常的。多线程
Runnable定义了run方法,而Callable定义了一个不带任何参数的叫作call的方法。异步
此外,Callable接口的类型参数也是返回值的类型。ide
public interface Callable {this
/**.net
* 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;
}
Future
Future表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并获取计算的结果。计算完成后只能使用 get 方法来获取结果,若有必要,计算完成前能够阻塞此方法。取消则由 cancel 方法来执行。还提供了其余方法,以肯定任务是正常完成仍是被取消了。一旦计算完成,就不能再取消计算。若是为了可取消性而使用 Future 但又不提供可用的结果,则能够声明 Future<?> 形式类型、并返回 null 做为底层任务的结果。
当使用Future对象时,你就能够启动一个计算,把计算结果给某线程,而后就去干本身的事。Future对象的全部者在结果计算好以后就能够获得它。
public interface Future {
boolean cancel(boolean mayInterruptIfRunning);
boolean isCancelled();
boolean isDone();
V get() throws InterruptedException, ExecutionException;
V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;
}
FutureTask
FutureTask包装器是一种很方便地将Callable转换为Future和Runnable的机制,它同时实现了二者的接口。例如:
Callable comput = …;
FutureTask task = new FutureTask(comput);
Thread t = new Thread(task); // it's a Runnable
t.start();
…
Integer result = t.get(); // it’s a Future
因此可以使用FutureTask包装Callable或Runnable对象。因为FutureTask实现了Runnable,可将 FutureTask提交给线程池的执行器类Executor执行。
使用Future+Callable+ExecutorService的例子
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class FutureTest {
public static class Task implements Callable {
private long id;
public Task(long id){
this.id = id;
}
@Override
public String call() throws Exception {
System.out.printf("Thread#%s : in call\n", this.id);
return this.id + "";
}
}
public static void main(String[] args) throws InterruptedException, ExecutionException {
List results = new ArrayList();
ExecutorService execService = Executors.newCachedThreadPool();
for(int i=0; i<10;i++)
results.add(execService.submit(new Task(i)));
for(Future res : results)
System.out.println(res.get());
}
}
可见,使用Future+Callable模式对于某些须要等待多个线程执行结果的场景很是有用,在HBase的batch操做中就使用了这种模式。