Android(Java)之多线程结果返回——Future 、FutureTask、Callable、Runnable

时间:2021-03-06 17:29:50


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方法来阻塞,直到任务结束


最后,贴上调用代码:

package com.example.androidfuturecallabledemo;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends Activity {

Button btnButton;
TextView txtTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btnButton=(Button)findViewById(R.id.btn);
txtTextView=(TextView)findViewById(R.id.txt);

btnButton.setOnClickListener(new OnClickListener() {

@Override
public void onClick(View v) {
try {
doSomeThing();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
});
}
private int i=0;
public void doSomeThing() throws InterruptedException, ExecutionException{
System.out.println("1 main thread ..."+" Thread id:"+Thread.currentThread().getId());
//Runnable
FutureThreadPool.getInstance().executeTask(new Runnable() {

@Override
public void run() {
try {
Thread.sleep(3*1000);
System.out.println("2 Runnable in FutureTask ..."+" Thread id:"+Thread.currentThread().getId());
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

}
});

//Callable
Future<String> futureTask= FutureThreadPool.getInstance().executeTask(new Callable<String>() {

@Override
public String call() throws Exception {
Thread.sleep(3*1000);
return "callable back return";
}
});
System.out.println("3 Callable in FutureTask ... Result:"+futureTask.get()+" Thread id:"+Thread.currentThread().getId());

//Runnable+T result
FutureTask<Integer> futureTask2=FutureThreadPool.getInstance().executeTask(new Runnable() {

@Override
public void run() {
i=7;
}
}, 9);

System.out.println("4 Callable and <T> in FutureTask ... Result:"+futureTask2.get()+" Thread id:"+Thread.currentThread().getId()+" i="+i);


FutureThreadPool.getInstance().executeFutureTask(new myFutrueTask(new Callable<String>() {

@Override
public String call() throws Exception {
// TODO Auto-generated method stub
String resu="5 SimpleFutureTask";
System.out.println("5 SimpleFutureTask ... Result:"+resu+" Thread id:"+Thread.currentThread().getId());
return resu;
}
}));
}

class myFutrueTask extends SimpleFutureTask<String>{

public myFutrueTask(Callable<String> callable) {
super(callable);
}

@Override
public void onFinish() {
System.out.println("6 SimpleFutureTask ...Finish");
}

}

}

运行,得到的结果如下:

Android(Java)之多线程结果返回——Future 、FutureTask、Callable、Runnable

注意点


在代码运行过程中,有个地方十分需要注意,那就是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