理解Android多线程里面三种任务Runnable和Callable和FutureTask的用法

1.Runnable 和Callable和FutureTask的区别

相同点:
都属于线程池中要被运行的任务
不同点:
Runnable是无返回值的任务,可以在线程中使用
Callable是有返回值的任务 ,不可以在线程中使用
FutureTask是有返回值,而且更易于管理和控制的任务,不可以在线程中使用;
前两者通过查看他们类可以很清楚的知道
public interface Runnable {
   /**这个任务运行完之后没有返回值*/
    public abstract void run();
}
public interface Callable<V> {
   /**这个任务运行完之后返回泛型 V*/
    V call() throws Exception;
}
而FutureTask稍微复杂一点,其实看看它的类结构 ,你也就明白了怎么回事
看到了吗,它也是一个Runnable的子类,这里还用到了一个Future类,其实想一下,Runnable是不可控制的任务,Future为这个任务提供了一套标准来管理任务;不信看看它的类
public interface Future<V> {
    /**取消这个任务执行,传递参数是true,表示停止正在执行的任务,否则,这行完这次任务*/
    boolean cancel(boolean mayInterruptIfRunning);
    /**任务是否被取消*/	
    boolean isCancelled();
    /**任务是否完成*/
    boolean isDone();
    /**获取任务的返回结果*/
    V get() throws InterruptedException, ExecutionException;
    /**获取任务的结果,还没有完成就等待*/
    V get(long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException;
}

三种任务的类图如下所示:

2.Runnable和Callable和FutureTask的执行过程分析

线程池ExecutorService里面提交任务的方法submit有如下三个重载

而在ExecutorService的抽象实现类AbstractExecutorService里面对着三个submit方法做了实现,你会发现他们都调用了Executor接口中的execute(Runnable command)方法;
至于这三个类的关系,看下图就知道,他们是继承关系
再来看看AbstractExecutorService类中三种submit方法源码的执行吧
 
    public Future<?> submit(Runnable task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<Void> ftask = newTaskFor(task, null);
        execute(ftask);
        return ftask;
    }
  
    public <T> Future<T> submit(Runnable task, T result) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<T> ftask = newTaskFor(task, result);
        execute(ftask);
        return ftask;
    }
 
    public <T> Future<T> submit(Callable<T> task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<T> ftask = newTaskFor(task);
        execute(ftask);
        return ftask;
    }

这三个方法都会:

1.先通过newTaskFor方法生成一个RunnableFuture的对象
2.然后在执行execute方法

生成newTaskFor方法过程

在看看newTaskFor方法是怎么回事;
 
    protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
        return new FutureTask<T>(runnable, value);
    }

    protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
        return new FutureTask<T>(callable);
    }
原来它返回的是FutureTask类型,原来不论我们传递到submit方法中的是那种任务,先是转换成FutureTask类型;再来看看FutureTask的构造方法吧!
   public FutureTask(Callable<V> callable) {
        if (callable == null)
            throw new NullPointerException();
        sync = new Sync(callable);
    }

    public FutureTask(Runnable runnable, V result) {
        sync = new Sync(Executors.callable(runnable, result));
    }
它构造出了一个 Sync对象,看看它的构造方法
        Sync(Callable<V> callable) {
            this.callable = callable;
        }
传递进去的是Callable类型的,即使是Runable也会被转化为Callable类型,我们在看看 Excutors.call()这个方法,它是将Runable转化为一个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;
        }
    }
Excutors.call这个方法会先将生成一个 RunnableAdapter,而这个类中含有Runnable的应用;
到此 submit方法中提交的任务最终都会被转化为Callable类型的任务
梳理一下 我们传递进来的不论是Runable和FutureTask类型,还是Callable类型任务,都统一构造成FutureTask类型,如果是Runable类型,而FutureTask构造内部会将它转为会Callable类型的变量传入FutureTask的构造;

execute方法的执行

到这里excute方法要执行的这个FutureTask对象,我们先想一下它又是什么?上面我们已经提到了,它呀实现了Runnable接口,它是一个线程,我们要执行的是一个线程!线程要看什么不就是run()方法?那么现在我们直接看看FutureTask类中实现的run方法不就是了;当然execute内部执行不看了,反正它是一个线程,是线程就要执行run()方法对吗?
看看FutureTask中的run()方法的源代码
  public void run() {
        sync.innerRun();
    }

看看就它一行代码,什么也不说了;往下继续走吧!
   void innerRun() {
            if (!compareAndSetState(READY, RUNNING))
                return;

            runner = Thread.currentThread();
            if (getState() == RUNNING) { // recheck after setting thread
                V result;
                try {
                    result = callable.call();
                } catch (Throwable ex) {
                    setException(ex);
                    return;
                }
                set(result);
            } else {
                releaseShared(0); // cancel
            }
        }
看到那一行 result = callable.call()代码了吗,当线程处于运行状态的时候会调用它。
submit提交任务的时候 FutureTask中持有Sync引用 ,而Sync中又持有Callable的引用,而此处的 callable就是我们的传递进来的Callable类型的对象,或者是Runnable类型的对象转换成的Callable吗;别忘了FutureTask继承Runnable的。
现在好办了

1、如果是我们自己实现了Callable接口,那么此处就会直接调用我们overvide的call()方法;看代码示例

        ExecutorService mExecutorService = Executors.newCachedThreadPool();
		mExecutorService.submit(new Callable<String>() {

			@Override
			public String call() throws Exception {
				System.out.println("我直接实现了Callable接口");
				return "111";
			}
		});

此时会直接调用自己的实现的call()方法;打印 System.out.println的内容

2、那么我是实现了Runnable接口的情况,那么再看分析吧!

是Runable的话,会被Excutors.call方法转变成Callable对象使用;当此处调用result = callable.call()的时候,这个callable是转化来的。这个callable又是怎么回事?上面也提到了,这里再看一眼Excutors.call是怎么转化的;
 
  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;
        }
    }

转化成的RunnableAdapter对象也是一个Callable的实现类; 这个时候会调用自己实现的call()方法;看看call()方法的内部task.run()这行代码 ,task会指向我们的Runable的对象,就相当于执行了Runnable接口的run()方法,,,

3、那么如果我submit中传递进去的FutureTask呢

按照我上面的Callable和Runnable套路分析,你会发现这全都是套路!在走一遍套路,先看一下例子代码;
        FutureTask<String> m1 = new FutureTask<String>(
				new Callable<String>() {
			@Override
			public String call() throws Exception {
				return null;
			}
		});
		mExecutorService.submit(m1);

		FutureTask<String> m2 = new FutureTask<String>(
			new Runnable() {
			@Override
			public void run() {


			}
		}, null);
		mExecutorService.submit(m2);
上面是FutureTask的两种构造;一个传递进去了Callable,另一个传递进去了Runable了;
分析
首先FutureTask实现了Runnable接口!
那么m1和m2他们都会被当成是Runnable参数构造成一个新的FutureTask任务,执行的时候会按照实现了Runnable接口的情况执行,m2就会执行自己的覆写的run方法。
但是m1没有覆写run()方法,那么它就会执行自身的run方法,执行到最后还是Callable调用自己的call方法;
这里在总结一下关系类图:

 

执行任务的流程图:

 

以上就是我对三种任务的理解,希望对大家有所帮助!


 

 
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值