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

理解三种任务Runnable和Callable和FutureTask的用法

1.Runnable 和Callable和FutureTask的区别

相同点:都属于线程池中要被运行的任务;
不同点:
Runnable是无返回值的任务,可以在线程中使用
Callable是有返回值的任务 ,不可以在线程中使用
FutureTask是有返回值,而且更易于管理和控制的任务,不可以在线程中使用;
前两者通过查看他们类可以很清楚的知道
public interface Runnable {
   /**这个任务运行完之后没有返回值*/
    public abstract void run();
}
public interface Callable {
   /**这个任务运行完之后返回泛型 V*/
    V call() throws Exception;
}
而FutureTask稍微复杂一点,其实看看它的类结构 ,你也就明白了怎么回事
看到了吗,它也是一个Runnable的子类,这里还用到了一个Future类,其实想一下,Runnable是不可控制的任务,Future为这个任务提供了一套标准来管理任务;不信看看它的类
public interface Future {
    /**取消这个任务执行,传递参数是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有如下三个重载,

理解Android多线程里面三种任务Runnable和Callable和FutureTask的用法_第1张图片

而在ExecutorService的抽象实现类AbstractExecutorService里面对着三个submit方法做了实现,你会发现他们都调用了Executor接口中的execute(Runnable command)方法;
至于这三个类的关系,看下图就知道,他们是继承关系
理解Android多线程里面三种任务Runnable和Callable和FutureTask的用法_第2张图片
再来看看AbstractExecutorService类中三种submit方法源码的执行吧
 
    public Future submit(Runnable task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture ftask = newTaskFor(task, null);
        execute(ftask);
        return ftask;
    }
  
    public  Future submit(Runnable task, T result) {
        if (task == null) throw new NullPointerException();
        RunnableFuture ftask = newTaskFor(task, result);
        execute(ftask);
        return ftask;
    }
 
    public  Future submit(Callable task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture ftask = newTaskFor(task);
        execute(ftask);
        return ftask;
    }
这三个方法都会:
1.先通过newTaskFor方法生成一个RunnableFuture的对象.
2.然后在执行execute方法.

1.生成newTaskFor方法过程

在看看newTaskFor方法是怎么回事;

    protected  RunnableFuture newTaskFor(Runnable runnable, T value) {
        return new FutureTask(runnable, value);
    }

    protected  RunnableFuture newTaskFor(Callable callable) {
        return new FutureTask(callable);
    }

原来它返回的是FutureTask类型,原来不论我们传递到submit方法中的是那种任务,先是转换成FutureTask类型;再来看看FutureTask的构造方法吧!

   public FutureTask(Callable 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 callable) {
            this.callable = callable;
        }
传递进去的是Callable类型的,即使是Runable也会被转化为Callable类型,我们在看看 Excutors.call()这个方法,它是将Runable转化为一个Callable
 public static  Callable callable(Runnable task, T result) {
        if (task == null)
            throw new NullPointerException();
        return new RunnableAdapter(task, result);
    }
  static final class RunnableAdapter implements Callable {
        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类型还是Callable类型任务,都统一构造成FutureTask类型(只接受Callable类型参数),如果是Runable类型,而FutureTask构造内部会将它转为会Callable类型的变量传入FutureTask的构造;

2.execute方法的执行

到这里excute方法要执行的这个FutureTask对象,我们先想一下它又是什么?上面我们已经提到了,它呀实现了Runnaable接口,不就是一个线程吗?我们要执行的是一个线程!线程要看什么不就是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吗;
现在好办了

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

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

			@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  Callable callable(Runnable task, T result) {
        if (task == null)
            throw new NullPointerException();
        return new RunnableAdapter(task, result);
    }
  static final class RunnableAdapter implements Callable {
        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的对象;

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

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


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


			}
		}, null);
		mExecutorService.submit(m2);
上面是FutureTask的两种构造;一个传递进去了Callable,另一个传递进去了Runable了;
分析
首先FutureTask实现了Runnable接口!
那么m1和m2他们都会被当成是Runnable参数构造成一个新的FutureTask任务,执行的时候会按照 2.那么我是实现了Runnable接口的情况,那么再看分析吧!这种情形分析的执行吧.
那么我这个m1和m2分别执行自己的run()方法,但是自己没有覆写run()方法,m1和m2都是FutureTask的对象。这时候你会发现
m1按照 1.如果是我们自己实现了Callable接口,那么此处就会直接调用我们overvide的call()方法;看代码示例 套路走!
m2按照 2.那么我是实现了Runnable接口的情况,那么再看分析吧!套路走!
是不是有点递归的感觉的,,,差点被绕晕了!

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







你可能感兴趣的:(理解Android多线程里面三种任务Runnable和Callable和FutureTask的用法)