Android、Java中Runnable十分常见,在开新线程时,我们常用new Thread(Runnable).start() 或者线程池搭载Runnable。
日常使用,在不需要线程返回时,使用的十分顺手。
在需要线程返回时,我们也有办法搞定,比如外部变量控制流程、新增监听接口等。
有了以上理由,Callable就被冷落了。
其实Callable能让你的实现以及代码更简单。本文就是以Callable为中心来介绍的。
一、Callable与Runnable
为什么Runnable用的人多,而Callable用的少?
1、Callable还没出现前,大家用的都是Runnable;(Callable是JDK5出现的)
2、Runnable用法更简单;
具体的区别如下:
1、结构
Callable接口是带有泛型的,Callable<T>。该泛型T,也是Callable返回值的类型;Callable接口需要实现的方法为call方法;
Runnable接口需要实现的方法为run方法;
2、使用
Callable一般配合线程池的submit方法以及FutureTask使用,Runnable一般是配合new Thread或者线程池使用;
3、返回
Callable有返回值,并且可以自定义返回值类型;Runnable不行;
4、控制
Callable配合FutureTask,可以通过Future来控制任务执行、取消,查看任务是否完成等。Runnable也可以通过Future来实现以上功能,但方式不一样。
二、Future以及FutureTask
Callable的价值,在Future上体现。
Future是一个接口,而FutureTask是Future接口的官方唯一实现类。
1、Future接口
Future以及其实现类,是用于搭载Runnable或者Callable,执行任务、控制任务并能有效返回结果。
Future接口内容如下(去了注释):
- package java.util.concurrent;
-
-
- 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;
-
- }
其中,isCancelled用于判断是否已取消任务、isDone用于判断是否已完成任务。
cancel用于取消任务,cancel的参数表示是否可以中断正在执行中的任务。参数解释如下:
任务未开始:无论设置参数为true还是false,都返回true;
任务正在执行,并未结束:参数设置为true,则返回true(成功取消),如果设置为false,则返回false(不允许中断正在执行的任务);
任务已结束:无论设置参数为true还是false,都返回false;
get方法用于获取任务执行的结果,get方法是一个阻塞方法,会等到任务执行完毕。
get(long timeout,TimeUnit unit)方法也是一个阻塞方法,等待任务执行的结果,但它只等到超时时间结束,如果任务还未执行完成,则返回一个null。
2、FutureTask类
FutureTask类不止实现了Future接口,还实现了其他的接口——Runnable,如下:
- public class FutureTask<V> implements RunnableFuture<V>
- public interface RunnableFuture<V> extends Runnable, Future<V>
因此,FutureTask其实也可以用于new Thread(FutureTask),当然也用于线程池。
FutureTask与Future接口相比,功能扩张了很多。
首先看它的构造函数:
- public FutureTask(Runnable runnable, V result)
- public FutureTask(Callable<V> callable)
看到这里,我们知道通过FutureTask,你可以传入Callable或者Runnable,而FutureTask则搭载二者。最后,FutureTask会将自身作为新开线程或者线程池的参数。
FutureTask有一个很重要的方法,是Done(),用于表示该FutureTask中的任务已执行完毕。后面会在代码中介绍。
三、实例解析
有这么一个场景:
你需要顺序的执行一系列任务,上一个任务是下一个任务的前置。下一个任务需要根据上一个任务的结果来判断是否执行。如果上一个任务失败则不再往下执行任务。
这些任务都是耗时的,你是在Android上执行这些任务的。
出现这个场景,在JDK5前,你用Runnable以及外部变量控制,是可以实现的。在JDK5以后,我们尝试用Callable配合FutureTask来实现。(Runnable配合Future也是可以的,只是不常用)。
根据场景,设计方案:
(1)串行线程池+Callable+FutureTask
(2)串行线程池+Runnable+FutureTask
(3)外部变量控制——不再演示
(4)全局监听——不再演示
这里演示的是1、2两种方案。
这里贴上为以上场景写的工具类和方法:
- package com.example.androidfuturecallabledemo;
-
- import java.util.concurrent.Callable;
- import java.util.concurrent.ExecutorService;
- import java.util.concurrent.Executors;
- import java.util.concurrent.FutureTask;
-
- public class FutureThreadPool {
-
- private FutureThreadPool(){}
- private volatile static FutureThreadPool futureThreadPool;
- private static ExecutorService threadExecutor;
- /**
- * 获取线程池实例(单例模式)
- * @return
- */
- public static FutureThreadPool getInstance(){
- if(futureThreadPool==null){
- synchronized (FutureThreadPool.class) {
- futureThreadPool=new FutureThreadPool();
- threadExecutor=Executors.newSingleThreadExecutor();
- }
- }
- return futureThreadPool;
- }
-
-
- /**
- * 线程池处理Runnable(无返回值)
- * @param runnable Runnable参数
- */
- public void executeTask(Runnable runnable){
- threadExecutor.execute(runnable);
- }
-
- /**
- * 线程池处理Callable<T>,FutureTask<T>类型有返回值
- * @param callable Callable<T>参数
- * @return FutureTask<T>
- */
- public <T> FutureTask<T> executeTask(Callable<T> callable){
- FutureTask<T> futureTask= new FutureTask<T>(callable);
- threadExecutor.submit(futureTask);
- return futureTask;
-
- }
- /**
- * 线程池处理Runnable,FutureTask<T>类型有返回值(该方法不常用)
- * @param Runnable参数
- * @param T Runnable任务执行完成后,返回的标识(注意:在调用时传入值,将在Runnable执行完成后,原样传出)
- * @return FutureTask<T>
- */
- public <T> FutureTask<T> executeTask(Runnable runnable,T result){
- FutureTask<T> futureTask= new FutureTask<T>(runnable,result);
- threadExecutor.submit(futureTask);
- return futureTask;
- }
- /**
- * 线程池处理自定义SimpleFutureTask,任务结束时有onFinish事件返回提示
- * @param mFutureTask 自定义SimpleFutureTask
- */
- public <T> FutureTask<T> executeFutureTask(SimpleFutureTask<T> mFutureTask){
- threadExecutor.submit(mFutureTask);
- return mFutureTask;
- }
-
-
- }
- package com.example.androidfuturecallabledemo;
-
- import java.util.concurrent.Callable;
- import java.util.concurrent.FutureTask;
- /**
- * 任务结束回调onFinish的添加
- * @author zhao.yang
- *
- * @param <T>
- */
- public abstract class SimpleFutureTask<T> extends FutureTask<T>{
-
- public SimpleFutureTask(Callable<T> callable) {
- super(callable);
- }
-
- @Override
- protected void done() {
- onFinish();
- }
-
- public abstract void onFinish();
-
-
- }
以上是创建的工具类,结合封装了Callable/Runnable、FutureTask以及线程池,方便调用。这里特别注意executeFutureTask方法,在该方法中,重写了done方法以及新增
onFinish抽象方法,可以通过回调onFinish,通知调用者任务执行结束。调用者,也可以通过FutureTask的get方法来阻塞,直到任务结束。
最后,贴上调用代码:
运行,得到的结果如下:

注意点
在代码运行过程中,有个地方十分需要注意,那就是FutureTask的其中一个重载方法:
- public FutureTask(Runnable runnable, V result)
在代码的调用中,我们传入的是一个整形i,i最初复制为0,在任务中被赋值为7,但是在参数中,我们传入的是9。看打印出来的信息我们知道,通过get方法,我们得到的值是9,而不是其他值。
看它在源码中的调用:
- public FutureTask(Runnable runnable, V result) {
- this.callable = Executors.callable(runnable, result);
- this.state = NEW; // ensure visibility of callable
- }
- public static <T> Callable<T> callable(Runnable task, T result) {
- if (task == null)
- throw new NullPointerException();
- 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;
- }
- }
在第三段代码中,你就懂的,这个T result,你传入什么,在任务结束时,就传回原值。
四、源码
源码地址:http://download.csdn.net/detail/yangzhaomuma/9554877
|