Android并发二三事之Callable,Future,FutureTask
Android 并发的第二篇,本篇还是要介绍Java 方向并发相关的知识点。
主要涉及Callable与 Runnable 的关系。 Callable 如何使用, Future 概念, Future 如何使用以及 Future 的实现类 FutureTask。
最后介绍如何利用Future 实现在子线程中开启子线程去请求网络。
一、Callable :
public interface Callable { /** * 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 中有具有一下几个接口:
Future submit(Callable task); Future 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 { 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 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); }
//自定义Callablepublic 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 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 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,实现更灵活的功能。
更多相关文章
- Android基于OpenGL的GLSurfaceView创建一个Activity实现方法
- 【转】Android-Action Bar使用方法
- Android更新Ui线程的四个方法
- Android网络层与数据层设计
- Android获取本机IP地址(不是localhost)和MAC的方法
- Android 导入android源码有错,R.java文件不能自动生成解决方法
- Git,SVN使用方法杂记(更新中)