您的位置:首页 > 移动开发 > Android开发

Android并发二三事之Callable,Future,FutureTask

2016-11-04 12:03 281 查看
Android 并发的第二篇,本篇还是要介绍Java 方向并发相关的知识点。

主要涉及Callable与 Runnable 的关系。 Callable 如何使用, Future 概念, Future 如何使用以及 Future 的实现类 FutureTask。

最后介绍如何利用Future 实现在子线程中开启子线程去请求网络。

一、Callable :

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;

}


Callable 本身是一个泛型接口, call()方法返回的类型,就是V的类型。

可能大家更熟悉的是:

Runnable接口:

public interface Runnable {

/**
* Starts executing the active part of the class' code. This method is
* called when a thread is started that has been created with a class which
* implements {@code Runnable}.
*/
public void run();
}


Callable 类似于Runnable 接口,用来封装一个任务,可以运行在一个线程之中。

只不过 Runnable 的 run() 方法没有返回值, 而 call() 方法具有返回值。

不过不同于 Runnable 的是,Callable 无法被封装到Thread中执行。

一般Callable 会与线程池一起使用。

在 ExecutorService 中有具有一下几个接口:

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

<T> Future<T> submit(Runnable task, T result);

Future<?> submit(Runnable task);


在上一篇博客中,我们介绍了利用Executors 提供的线程池,执行联网等耗时任务:

public void request() {

ExecutorService executor = Executors.newFixedThreadPool(5);
executor.execute(new Runnable() {
@Override
public void run() {
try {
//请求网络等。
} catch (Exception e) {
e.printStackTrace();
}
}
});

}


这里其实除了execute() 方法还有一个方法可以用,那就是submit()方法。

只不过submit()方法是有返回值的,返回值就是Future 。

二、Future 接口:

public interface Future<V> {

boolean cancel(boolean mayInterruptIfRunning);

boolean isCancelled();

boolean isDone();

V get() throws InterruptedException, ExecutionException;

V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}


Future 可以用来获取Runnable , Callable 任务的执行结果,查询等。

一般Future 模式可以这样理解:

我有一个任务交给Future,让Future 替我去完成。在这个期间我可以做别的事情,一段时间之后,我便可以从Future 那里获取结果。

Future 一般和线程池一起使用,利用 ExecutorService 的 submit() 方法,就可以返回一个Future 对象。

Future接口提供方法来检测任务是否被执行完,等待任务执行完获得结果,也可以设置任务执行的超时时间。这个设置超时的方法就是实现Java程 序执行超时的关键。

接口的方法介绍如下:

boolean cancel (boolean mayInterruptIfRunning) 取消任务的执行。参数指定是否立即中断任务执行,或者等等任务结束

boolean isCancelled () 任务是否已经取消,任务正常完成前将其取消,则返回 true

boolean isDone () 任务是否已经完成。需要注意的是如果任务正常终止、异常或取消,都将返回true

V get () throws InterruptedException, ExecutionException 等待任务执行结束,然后获得V类型的结果。InterruptedException 线程被中断异常, ExecutionException任务执行异常,如果任务被取消,还会抛出CancellationException

V get (long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException 同上面的get功能一样,多了设置超时时间。参数timeout指定超时时间,uint指定时间的单位,在枚举类TimeUnit中有相关的定义。如果计 算超时,将抛出TimeoutException

三、FutureTask:

FutureTask 即为Future 的实现, FutureTask 不仅实现了 Future, 还实现了 Runnable 所以其也可以作为一个Runnable 提交给线程池。

submit() 方法为我们返回的其实就是 FutureTask 对象。

四、以下为利用Future获取数据的例子:

//请求数据
public void requestCar() {

ExecutorService executor = Executors.newFixedThreadPool(5);
Future<ResponInfo> future = executor.submit(new Task());
try{
ResponInfo info = future.get();
Log.d(TAG,"result : "+info.getName());
}catch (Exception e){

}

}

//获取数据
private ResponInfo requestInfo() {
try {
//模拟连接网络耗时
Thread.sleep(3000);
}catch (Exception e) {

}
return new ResponInfo("BMW", 2000);
}


//自定义Callable
public class Task implements Callable<ResponInfo> {

@Override
public ResponInfo call() throws Exception {

return requestInfo();
}
}


//数据实体
public class ResponInfo {

private String name;
private long price;

public ResponInfo(String name, long price) {
this.name = name;
this.price = price;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public long getPrice() {
return price;
}

public void setPrice(long price) {
this.price = price;
}
}


具体来看requestCar() 方法:

public void requestCar() {

ExecutorService executor = Executors.newFixedThreadPool(5);
Future<ResponInfo> future = executor.submit(new Task());
//在这里我们可以做其他的事情。
try{
//调用future.get()方法后,如果结果已经返回,那么将直接返回结果。
//如果结果还没有返回,那么当前线程将会被阻塞,以等待结果的返回。
ResponInfo info = future.get();
Log.d(TAG,"result : "+info.getName());
}catch (Exception e){

}

}


我们还可以加入超时时间:

public void requestCar() {

ExecutorService executor = Executors.newFixedThreadPool(5);
Future<ResponInfo> future = executor.submit(new Task());
try{
//2秒内不返回,将抛出异常。
ResponInfo info = future.get(2, TimeUnit.SECONDS);
Log.d(TAG,"result : "+info.getName());
}catch (Exception e){
e.printStackTrace();
//取消任务
future.cancel(true);
Log.d(TAG, "cancel ");
}

}


五、在第一篇中说过,如何在子线程中再开启子线程请求网络数据。

其实在子线程中请求数据不难,但是如果我们在子线程中又开启了一个线程去请求数据呢?

例如当我们需要在子线程中,利用Volley 请求数据。我们知道Volley会另外开启子线程去连接网络。

如果发生这样的情况,我们需要让当前的线程阻塞,等待数据的返回,否则根本得不到数据的。

在第四步中Future.get()方法会阻塞当前的线程等待数据的返回,刚好符合我们的需求。

OK , 这篇就介绍到这里。

下一篇会介绍如何自定义Future,实现更灵活的功能。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息