Android进阶——多线程系列之ThreadRunnableCallableFutureFutureTask
Posted Hensen_(许市长)
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android进阶——多线程系列之ThreadRunnableCallableFutureFutureTask相关的知识,希望对你有一定的参考价值。
前言
多线程一直是初学者最抵触的东西,如果你想进阶的话,那必须闯过这道难关,特别是多线程中Thread、Runnable、Callable、Future、FutureTask这几个类往往是初学者容易搞混的。这里先总结这几个类特点和区别,让大家带着模糊印象来学习这篇文章
- Thread、Runnable、Callable:都是线程
- Thread特点:提供了线程等待、线程睡眠、线程礼让等操作
- Runnable和Callable特点:都是接口,并提供对应的实现方法
- Runnable、Callable区别:Runnable无返回值,Callable有返回值
- Future:提供了对Runnable和Callable任务的执行结果进行取消、查询是否完成、获取结果、设置结果等操作
- FutureTask:Runnable和Future的结合体,即拥有Future的特性
Thread和Runnable的关系
我们对线程的使用,经常有这两种写法
new Thread(new Runnable() {
@Override
public void run() {
//子线程操作
}
}).start();
new Thread(){
@Override
public void run() {
//子线程操作
}
}.start();
这也就是我们要讲的Thread和Runnable的关系,其实它们是一样的,都是线程,最终启动线程后都会执行run()方法里面的内容,具体是怎么一回事,让我们从Thread的源码开始分析
class Thread implements Runnable {
private Runnable target;
//构造函数
public Thread(ThreadGroup group, Runnable target) {
init(group, target, "Thread-" + nextThreadNum(), 0);
}
//继续追踪init()方法
private void init(ThreadGroup g, Runnable target, String name, long stackSize) {
Thread parent = currentThread();
if (g == null) {
g = parent.getThreadGroup();
}
g.addUnstarted();
this.group = g;
this.target = target;
this.priority = parent.getPriority();
this.daemon = parent.isDaemon();
setName(name);
init2(parent);
/* Stash the specified stack size in case the VM cares */
this.stackSize = stackSize;
tid = nextThreadID();
}
}
可以看到Thread就是实现Runnable的,所以Thread也可以说是Runnable,它俩就像亲生兄弟一样。由于我们创建线程的时候第一步都是new Thread()开始的,所以看到Thread的构造函数,继续追踪之后,你会发现如果传进来一个Runnable的话,会被赋予target的成员变量中,而target就是一个Runnable。接着,调用Thread的start()方法,我们查看start()方法的源码
public synchronized void start() {
if (threadStatus != 0)
throw new IllegalThreadStateException();
group.add(this);
started = false;
try {
//最终调用这个方法
nativeCreate(this, stackSize, daemon);
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
}
}
}
//继续追踪nativeCreate()方法
private native static void nativeCreate(Thread t, long stackSize, boolean daemon);
我们可以看到start()方法在最后是调用了本地底层的nativeCreate()方法,这个方法我们大胆的猜测,应该是执行Thread里面的run()方法,因为我们开启线程之后,线程总是会执行run()里面的内容,所以这里应该就是调用了Thread的run()方法。我们查看run()方法的源码
@Override
public void run() {
if (target != null) {
target.run();
}
}
到这里,实际上最终被线程执行的任务是Runnable而非Thread,Thread只是对Runnable的包装。如果target不为空则执行target的run()方法,否则执行当前对象的run()方法
Runnable和Callable的区别
Runnable和Callable同样都是线程,而它们的区别可以从其源码中看出来,下面是Runnable和Callable的源码
public interface Runnable {
public abstract void run();
}
@FunctionalInterface
public interface Callable<V> {
V call() throws Exception;
}
可以发现,Callable带有一个泛型的返回值,而Runnable并没有返回值,所以使用Callable可以返回线程的结果,而Runnable不行
Future
Future不是线程,它可以理解为管理线程的人,不过它对线程的管理方法不是很多,这点可以从Future的源码中看出来,下面是Future的源码
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;
}
关说不练假把式,我们通过例子来理解,这里的线程我们都使用线程池来执行,如果对线程池不理解的,可以查看我的博客Android进阶——多线程系列之四大线程池的使用介绍
public class ThreadActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_thread);
//线程池
ExecutorService executor = Executors.newFixedThreadPool(10);
//第一步:创建线程
MyRunnable myRunnable = new MyRunnable();
MyCallable myCallable = new MyCallable();
try {
//第二步:执行线程带有返回值
Future<Integer> CallableFuture = executor.submit(myCallable);
//get()方法会使线程阻塞
System.out.println("CallableFuture获取的结果:" + CallableFuture.get());
System.out.println("CallableFuture isCancelled(是否已经取消):" + CallableFuture.isCancelled());
System.out.println("CallableFuture isDone(是否已经完成):" + CallableFuture.isDone());
//第二步:执行线程没有返回值
Future<?> RunnableFuture = executor.submit(myRunnable);
System.out.println("RunnableFuture获取的结果:" + RunnableFuture.get());
System.out.println("RunnableFuture isCancelled(是否已经取消):" + RunnableFuture.isCancelled());
System.out.println("RunnableFuture isDone(是否已经完成):" + RunnableFuture.isDone());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
public class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("我是Runnable");
}
}
public class MyCallable implements Callable<Integer> {
@Override
public Integer call() throws Exception {
System.out.println("我是Callable");
return 10;
}
}
}
第一步:创建线程MyCallable中是实现Callable的,泛型里面填返回值的类型,而Runnable则不用
第二步:通过线程池executor提交线程,返回对应的Future< ?>对象,若有返回值则填返回值类型,若无返回值则填?号
下面我们通过打印信息来查看Future管理类的执行结果,再一次的证明:Runnable和Callable的区别
我是Callable
CallableFuture获取的结果:10
CallableFuture isCancelled(是否已经取消):false
CallableFutureisDone(是否已经完成):true
我是Runnable
RunnableFuture获取的结果:null
RunnableFuture isCancelled(是否已经取消):false
RunnableFuture isDone(是否已经完成):true
FutureTask
FutureTask是Future的实现类,而且不仅是Future又是Runnable,还包装了Callable,它是这两者的合体。从FutureTask的源码可以看出
//继续追踪RunnableFuture
public class FutureTask<V> implements RunnableFuture<V> {
//构造方法
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW;
}
//构造方法
public FutureTask(Runnable runnable, V result) {
this.callable = Executors.callable(runnable, result);
this.state = NEW;
}
}
//RunnableFuture继承Runnable、Future
public interface RunnableFuture<V> extends Runnable, Future<V> {
void run();
}
源码中可以看出构造函数中需要传进去一个Callable或者是Runnable,如果传进去是个Runnable则会被Executors.callable()转换为Callable类型的任务,即FutureTask最终都是执行Callable类型的任务。继续追踪Executors.callable()
//Executors.callable()方法
public static <T> Callable<T> callable(Runnable task, T result) {
if (task == null)
throw new NullPointerException();
return new RunnableAdapter<T>(task, result);
}
//继续追踪RunnableAdapter<T>类
private static final class RunnableAdapter<T> implements Callable<T> {
private final Runnable task;
private final T result;
RunnableAdapter(Runnable task, T result) {
this.task = task;
this.result = result;
}
public T call() {
task.run();
return result;
}
}
由于FutureTask实现了Runnable,因此,它既可以通过Thread包装来直接运行,也可以通过给ExecuteService来执行,并且还可以通过get()方法获取执行结果,该函数会阻塞,直到结果返回。下面通过简单的例子演示FutureTask的用法
public class FutureTaskActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_future_task);
//线程池
ExecutorService executor = Executors.newFixedThreadPool(10);
//创建线程
MyCallable callable = new MyCallable();
//包装线程
FutureTask<String> futureTask = new FutureTask<String>(callable);
//执行线程
executor.submit(futureTask);
//获取结果
try {
System.out.println(futureTask.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
public class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
Thread.sleep(2000);
return "我是Callable返回值";
}
}
}
打印信息是
我是Callable返回值
以上是关于Android进阶——多线程系列之ThreadRunnableCallableFutureFutureTask的主要内容,如果未能解决你的问题,请参考以下文章
Kotlin基础从入门到进阶系列讲解(进阶篇)Android之Activity的生命周期
Java知识体系Java并发编程进阶,多线程和锁底层原理探究