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,实现更灵活的功能。