转载注明出处:http://www.jianshu.com/p/c931df278244
简介
本篇主要针对AsyncTask中使用到的多线程知识进行讲解,也会涉及到一些基本的多线程知识。在上一篇中大家都知道了AsyncTask内部会起一个非UI线程去执行一些比较耗时的操作,那么这个线程在AsyncTask是怎么样被执行的,仅仅只是实例化一个Thread然后启动它吗?在内部对线程是怎么样处理的?这些都需要从AsyncTask的源代码中才能查看到,在看源代码之前先讲解一下关于AsyncTask内部涉及到的一些多线程知识,方便后面更快速的理解源代码,磨刀不误砍柴工!
线程之Thread&Runnable
说到多线程,通常很快就能想到Thread
类和Runnable
接口,相信大家并不陌生,直接说一下二者的区别吧。
- 类和接口的区别,在java中只能单一的继承,但是可以实现多个接口,所以使用Runnable更灵活
- 使用Runnable可以实现资源共享
- Thread实现了Runnable接口
看起来好像Runnable
非常好用,那么都用它不就行了吗?为什么要需要Thread
呢?是因为Thread
的start()
方法是调用了本地的一个系统方法,来创建一个线程,这个才是实现多线程的根本,而run()
方法只是线程中执行的具体任务,这也是在创建多线程时候,要调用start()
方法而不是直接调用run()
方法的原因。
使用起来也非常简单,看下面代码。因为Runnable仅仅是个接口,并没有start()
方法,所以会依托于Thread。
//Thread使用
new Thread() {
@Override
public void run() {
//具体操作
}
}.start();
//Runnable使用
Runnable r = new Runnable() {
@Override
public void run() {
//具体操作
}
};
new Thread(r).start();
线程之Callable&Future&FutureTask
上面讲述了创建线程的两种方式,一种是继承Thread
,另一种是实现Runnbale
接口。这两种方式的问题在于,在执行完毕任务之后,无法获取执行的结果,也就是说,如果我们需要一个能够返回执行结果的线程,上面两种方式想要实现这种效果就需要通过共享资源或者使用线程间通信,这就比较麻烦了。
在java 1.5的时候,官方推出了Callable
接口、Future
接口、FutureTask
类,可以帮助我们获取线程的执行结果。下面就详细的说明一下这两个接口和一个类。
-
Callable
接口定义了一个可以返回结果的任务,但是可能会抛出异常,和Runnable
接口有些类似,它们的实例可能被另一个线程执行,区别在于Callable
能够返回执行结果,而且可能抛出异常。Runnable
接口声明了一个run()
方法,Callable
接口声明了一个带返回结果的call()
方法public interface Callable<V> { V call() throws Exception; }
-
Future
接口表示异步操作执行完毕后返回的结果,该接口内部声明的方法用于检测异步操作是否完成、等待异步操作完成、获取异步操作结果。获取操作的执行结果只能通过get()
方法,如果操作已经执行完毕,就直接返回结果,如果操作还在执行,就会一直阻塞,等待操作执行完毕。Future
接口内部声明了五个方法,都很好理解。public interface Future<V> { boolean cancel(boolean mayInterruptIfRunning); boolean isCancelled(); boolean isDone(); V get(); V get(long timeout, TimeUnit unit); }
-
FutureTask
类实现了RunnableFuture<V>
接口,而RunnableFuture<V>
继承了Runnable
和Future<V>
接口,也就是说FutureTask
既可以当做一个线程具体的执行任务也可以当做一个线程具体任务的执行结果。它是一个具体的实现类,具体代码就不贴了。
我们来看一下具体的使用吧。
try {
ExecutorService executorService = Executors.newCachedThreadPool();
Future<String> future = executorService.submit(new Callable<String>() {
@Override
public String call() throws Exception {
return "result";
}
});
String result = future.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
实例化了一个ExecutorService
类,并且传入了一个匿名Callable
类,然后定义了一个Future
指向了任务执行完毕后返回的结果,最后调用Future.get()
方法获取返回结果。
前面说了,FutureTask
实现了Runnable
接口和Future
接口,它既可以作为一个线程内部具体的执行任务,又可以座位线程内部具体任务执行完毕后返回的结果。所以还可以这样用。
try {
Executor executor = Executors.newCachedThreadPool();
FutureTask<String> futureTask = new FutureTask<String>(new Callable<String>() {
@Override
public String call() throws Exception {
return "result";
}
});
executor.execute(futureTask);
String result = futureTask.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
首先实例化了一个Executor
,然后实例化了一个FutureTask
类,并将一个匿名的Callable
类传入,然后将FutureTask
当做是Runnable
用Executor
去执行,最后调用FutureTask.get()
方法获取执行结果。
总结
通过上面的介绍,在不使用内存共享或者线程间通信技术前提下,我们可以直接获取线程执行完毕后返回的结果,大大的降低了研发的成本。
一般是通过Calllable
+Future
或者Callable
+FutureTask
来实现这种效果,当然还有更多的使用方法,同时在Future
和FutureTask
使用过程也有很多细节问题,但是这篇文章主要是为了理解AsyncTask
源代码实现而进行的磨刀的讲解,所以并不深入,而下一篇就是真正的砍柴了,也终于可以初窥AsyncTask
内部的实现了。
网友评论