Runnable,Callable,Future和FutureTask简介

1.Runnable和Callable的使用

在Android开发时,如果我们需要将一个任务放在工作线程中运行,我们经常会先实现Runnable接口,然后将这个任务交给工作线程执行。例如:

1)实现Runnable接口

 private static class MyRunnable implements Runnable {
        @Override
        public void run() {
            try {
                TimeUnit.SECONDS.sleep(2);//模拟耗时任务
                System.out.println("runnable task is finished.");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

2)启动线程来执行这个任务

private void startRunnableTask() {
        new Thread(new MyRunnable()).start();
    }

3)结果如下

System.out: runnable task is finished.

下面我们来看看如果通过Callable接口来实现这个任务,需要哪些步骤

1)实现Callable接口

private static class MyCallable implements Callable<String> {
        @Override
        public String call() throws Exception {
            try {
                TimeUnit.SECONDS.sleep(2);//模拟耗时任务
                System.out.println("runnable task is finished.");
                return "callable result";
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }
    }

在这里我们可以看到,Callable接口跟Runnable类似,都只有一个方法需要实现,只不过call()方法有返回值。

2)通过线程来启动任务

 private void startFutureTask() {
        MyCallable callable = new MyCallable();
        futureTask = new FutureTask<String>(callable);//使用callable来创建一个futureTask
        new Thread(futureTask).start(); //由于futureTask是一个Runnable,所以可以用来生成线程
        try {
            String result = futureTask.get();//该函数阻塞,所以需要在线程中执行,在这里为了看的清楚,直接放在UI线程上了
            System.out.println("Result = " + result);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

3)运行结果

System.out: runnable task is finished.
System.out: Result = callable result

通过这个例子,我们可以看到,通过FutureTask的get()函数,我们可以获取到任务执行后的返回值。

2.Future和FutureTask源码分析

那么什么是FutureTask呢?实际上它是Future的一个唯一默认实现,而Future是一个接口,定义如下:

public interface Future<V> {
    //取消该任务,如果这个任务已经完成,或者已经取消,那么会返回false,否则返回true
    boolean cancel(boolean mayInterruptIfRunning);
    //如果在任务完成前取消了,那么返回true
    boolean isCancelled();
    //如果这个任务完成了,那么返回true
    boolean isDone();
    //如果任务完成了,那么返回返回值,否则等待完成,然后将值返回
    V get() throws InterruptedException, ExecutionException;
    //在指定时间内,如果任务完成了,则返回返回值,否则报超时异常
    V get(long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException;
}

通过上面的接口,我们可以看到,我们可以通过Future接口对运行的任务进行状态监控以及取消任务执行,返回任务执行结果等。

1)FutureTask的定义

public class FutureTask<V> implements RunnableFuture<V>

2)RunnableFuture的定义

public interface RunnableFuture<V> extends Runnable, Future<V> {
    //实际上,工作线程如果完成了该任务,那么设置完成结果,这个结果可以通过FutureTask的get()函数返回
    void run();
}

3)由于在FutureTask里,工作线程执行的时候还是在线程中直接运行run()函数,那么我们来简单看看FutureTask到底做了些什么

public void run() {
        if (state != NEW ||//如果这个任务状态不是一个新任务,那么直接返回
            !U.compareAndSwapObject(this, RUNNER, null, Thread.currentThread()))
            return;
        try {
            Callable<V> c = callable;//这个callable是通过构造函数赋值的callable,具体可以看上面的例子
            if (c != null && state == NEW) {//如果是一个新任务并且callable不为空
                V result;
                boolean ran;
                try {
                    result = c.call();//直接调用callable的call()函数,由于call()是在工作线程中操作了,所以不会造成ANR
                    ran = true;//设置执行成功
                } catch (Throwable ex) {
                    result = null;
                    ran = false;
                    setException(ex);
                }
                if (ran)
                    set(result);//执行成功,则设置返回结果
            }
        } finally {
            ....
        }
    }

然后我们看看set(result)的函数实现如下:

 protected void set(V v) {
        if (U.compareAndSwapInt(this, STATE, NEW, COMPLETING)) {
            outcome = v;//设置返回值
            U.putOrderedInt(this, STATE, NORMAL); // final state
            finishCompletion();//任务完成后的一些资源释放等操作,在这个函数里面调用了done()回调函数,我们实现上可以通过重写done()函数,在这个函数里面调用get()操作,这样就不会导致ANR
        }
    }

然后我们看看FutureTask是如何获取返回值的

public V get() throws InterruptedException, ExecutionException {
        int s = state;
        if (s <= COMPLETING)
            s = awaitDone(false, 0L);
        return report(s);//在这里返回返回值,
    }
private V report(int s) throws ExecutionException {
        Object x = outcome;//在设置函数set(V v)设置的值
        if (s == NORMAL)
            return (V)x;//返回该结果值
        if (s >= CANCELLED)
            throw new CancellationException();
        throw new ExecutionException((Throwable)x);
    }

3.FutureTask的正确使用姿势

1)实现代码

 private void startFutureTask() {

        MyCallable callable = new MyCallable();
        System.out.println("start " + new SimpleDateFormat("yyyy-MM-dd HH-mm-ss").format(new Date()));
        futureTask = new FutureTask<String>(callable){
            @Override
            protected void done() {
                try {
                    String result = futureTask.get();
                    System.out.println("Result = " + result);
                    System.out.println("complete " + new SimpleDateFormat("yyyy-MM-dd HH-mm-ss").format(new Date()));
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        };
        new Thread(futureTask).start();
        System.out.println("end " + new SimpleDateFormat("yyyy-MM-dd HH-mm-ss").format(new Date()));
    }

这段代码跟之前代码最主要的区别是,我们将获取任务结果放在done()函数里面。这样做的好处在于,由于futureTask的get()函数是阻塞的,如果直接放在UI线程,可能导致ANR。而done()函数则是在工作线程中的,所以不会导致ANR。

2)执行结果

System.out: start 2017-03-21 18-45-00
System.out: end 2017-03-21 18-45-00  
System.out: runnable task is finished.
System.out: Result = callable result
System.out: complete 2017-03-21 18-45-02 //这里可以看到这个任务是在2秒后完成的

4.Callable与Runnable的异同

1)Runnable不能获取任务执行结果以及抛出任务检查异常,而Callable则可以
2)Callable需要使用FutureTask封装,然后才可以交给线程执行,而Runnable不用
3)Callable,Runnable的任务都要先转化成RunnableFuture任务,然后才可以交给线程池执行

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值