在多线程开发中,一般都会使用Runnable
来处理异步任务,但是它有一定的缺陷
1、不能返回一个返回值
2、也不能抛出
checked Exception
接着就可以引入下面主题,Callable
和Future
,它在Android源码中很多地方使用到了,比如AsyncTask
中,学会了Callable
和Future
,就可以更方便看源码。
它是一个接口,约定了线程要做的事情,作用:
1、类似于Runnable,被其它线程执行的任务
2、实现call方法
3、有返回值
4、可以抛出异常
Callable一般和Future一起使用,不会单独使用
方法的计算可能很耗时,计算的过程中,没有必要在原地等返回,子线程执行过程中可以取做其它事情,执行完成,Future可以获取计算的结果。
我们可以用Future.get来获取Callable接口返回的执行结果,还可以通过Future.isDone()来判断任务是否已经执行完了,以及取消这个任务,限时获取任务的结果等
在call()方法未执行完毕之前,调用get()的线程(假定此时是主线程)会被阻塞
,直到call()方法返回了结果后,此时future.get()才会得到该结果,然后主线程才会切换到runnable状态
所以Future是一个存储器
,它存储了call()这个任务的结果,而这个任务的执行时间是无法提前确定的,因为这完全取决于call()方法执行的情况
get方法的行为取决于Callable任务的状态,只有以下5种情况
1、任务正常完成
:get方法会立刻返回
2、任务尚未完成
(任务还没开始或进行中):get将会阻塞
并直到任务完成
3、任务执行过程中Execption
:get方法会抛出ExecutionException
:这里抛出异常,是call()执行时产生的那个异常,看到这个异常类型时java.util,concurrent.ExecutionException。不论call()执行时抛出的异常类型是什么,最后get方法抛出的异常类型都是ExecutionException。
4、任务被取消
:get方法会抛出CancellationException
5、任务超时
:get方法有一个重载方法,是传入一个延迟时间的,如果时间到了还没有获得结果,get方法就会抛出TimeoutException
超时
的需求很常见
用get(long timeout, TimeUnit unit)方法时,如果call()在规定时间内完成任务,那么就会正常获取到返回值;而如果再指定时间内没有计算出结果,那么就会抛出TimeoutException
超时不获取,任务需取消
取消任务的执行
判断线程是否执行完毕
判断是否被取消
几种常用的用法
首先,我们要给线程池提交我们的任务,提交时线程池就会立刻返回
给我们一个空的Future
容器。当线程的任务一旦执行完毕,也就是当我们可以获取结果的时候,线程池便会把该结果填入
到之前给我们的那个Future中去(而不是创建一个新的Future),我们此时便可以从该Future中获得任务执行的结果
。
//演示一个Future的使用方法
@Test
public void test02(){
ExecutorService service = Executors.newFixedThreadPool(10);
Future<Integer> future = service.submit(new CallableTask());
try {
System.out.println(future.get());
} catch (ExecutionException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
service.shutdown();
}
class CallableTask implements Callable<Integer>{
@Override
public Integer call() throws Exception {
Thread.sleep(3000);
return new Random().nextInt();
}
}
1、Callable定义我们需要做的事情,是可以有返回值的
2、传入Callable任务,可以获取Future返回值
3、通过Future.get可以获取子线程执行结果
//演示批量提交任务时,用List来批量获取结果
@Test
public void test03() throws InterruptedException {
ExecutorService service = Executors.newFixedThreadPool(20);
ArrayList<Future> futures = new ArrayList<>();
for (int i = 0; i < 20; i++) {
Future<Integer> future = service.submit(new CallableTask());
futures.add(future);
}
Thread.sleep(5000);
for (int i = 0; i < 20; i++) {
Future<Integer> future = futures.get(i);
try {
Integer integer = future.get();
System.out.println(integer);
} catch (ExecutionException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class CallableTask implements Callable<Integer>{
@Override
public Integer call() throws Exception {
Thread.sleep(3000);
return new Random().nextInt();
}
}
//演示get方法过程中抛出异常,for循环为了演示抛出Exception的时机:并不是说一产生异常就抛出,直到我们get执行时,才会抛出。
@Test
public void test04(){
ExecutorService service = Executors.newFixedThreadPool(20);
Future<Integer> future = service.submit(new CallableTask04());
try {
for (int i = 0; i < 5; i++) {
System.out.println(i);
Thread.sleep(500);
}
System.out.println(future.isDone());
future.get();
} catch (ExecutionException e) {
e.printStackTrace();
System.out.println("ExecutionException异常");
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println("InterruptedException异常");
}
}
class CallableTask04 implements Callable<Integer>{
@Override
public Integer call() throws Exception {
throw new IllegalArgumentException("Callable抛出异常");
}
}
执行get才会抛出异常
private final Ad DEFAULT_AD = new Ad("无网络时候的默认广告");
private final ExecutorService service = Executors.newFixedThreadPool(2);
class FetchAdTask implements Callable<Ad>{
@Override
public Ad call() throws Exception {
try{
Thread.sleep(3000);
}catch (InterruptedException e){
System.out.println("sleep期间被中断了");
return new Ad("被中断时候的默认广告");
}
return new Ad("淘宝广告");
}
}
public void printAd(){
Future<Ad> f = service.submit(new FetchAdTask());
Ad ad;
try {
ad = f.get(2000, TimeUnit.MILLISECONDS);
} catch (ExecutionException e) {
e.printStackTrace();
ad = new Ad("异常时候的默认广告");
} catch (InterruptedException e) {
e.printStackTrace();
ad = new Ad("中断时候的默认广告");
} catch (TimeoutException e){
// e.printStackTrace();
ad = new Ad("异常时候的默认广告");
System.out.println("超时了,未获取到广告");
//cancel true 中断 false不会中断
boolean cancel = f.cancel(true);
System.out.println("cancel的结果" + cancel);
}
service.shutdown();
System.out.println(ad);
}
//演示get的超时方法,需要注意超时后处理,调用future.cancel()。演示cancel传入true和false的区别,代表是否中断正在执行的任务。
@Test
public void test05(){
printAd();
}
1、如果这个任务还没有开始执行
,那么这种情况最简单,任务会被正常的取消,未来也不会被执行,方法返回true
2、如果任务已完成
,或者已取消
,那么cancel()方法会执行失败,方法返回false
3、如果这个任务已经开始执行了
,那个这个取消方法将不会直接取消该任务,而是会根据我们填的参数mayInterruptIfRunning做判断;
Future.cancel(true)适用于:
1、任务能够处理interrupt
Future.cancel(false)仅用于避免启动尚未启动的任务,适用于:
1、未能处理interrupt的任务
2、不清除任务是否支持取消
3、需要等待已经开始的任务执行完成
FutureTask是一种包装器
,可以把Callable转化成Future和Runnable,它同时实现二者的接口
用Callable实例当作参数,生成FutureTask的对象,然后把这个对象当作一个Runnable对象,用线程池或另起线程区执行这个Runnable对象,最后通过FutureTask获取刚才执行的结果。
/**
* 描述: 演示FutureTask的用法
*/
public class FutureTaskDemo {
public static void main(String[] args) {
Task task = new Task();
FutureTask<Integer> integerFutureTask = new FutureTask<>(task);
// new Thread(integerFutureTask).start();
ExecutorService service = Executors.newCachedThreadPool();
service.submit(integerFutureTask);
try {
System.out.println("task运行结果:"+integerFutureTask.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
class Task implements Callable<Integer> {
@Override
public Integer call() throws Exception {
System.out.println("子线程正在计算");
Thread.sleep(3000);
int sum = 0;
for (int i = 0; i < 100; i++) {
sum += i;
}
return sum;
}
}
1、当for循环获取future的结果时,容易发生一部分线程很慢的情况,get方法调用时应使用timeout限制
2、Future的生命周期不能后退
生命周期只能前进,不能后退。就和线程池的生命周期一样,一旦完全完成了任务,它就永远停在了“已完成”的状态,不能重来。
使用完之后,开始分析源码
接口定义:
public interface Callable<V> {
V call() throws Exception;
}
返回值是一个泛型,可以定义成为任何类型,并且可以看到是可以抛出异常的,在向线程池提交任务时,可以提交Runnable
和Callable
两种任务类型,
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(Callable<T> task) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task);
execute(ftask);
return ftask;
}
这两种任务都会转化为Callable
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);
}
RunnableAdapter
是Callable
一个实现,然后会包装到FutureTask
中,线程运行的具体任务是通过它, FutureTask
实现了 RunnableFuture
接口
public class FutureTask<V> implements RunnableFuture<V> {
}
而 RunnableFuture
又实现了 Runnable
, Future
两个接口。
Callable 是可以返回子线程执行结果的,在获取结果的时候,就需要用到 Future接口
Future接口注释写了这些:
1、定义了异步计算的接口,提供了计算是否完成的 check、等待完成和取回等多种方法;
2、如果想得到结果可以使用 get 方法,此方法(无参方法)会一直阻塞到异步任务计算完成;
3、取消可以使用 cancel 方法,但一旦任务计算完成,就无法被取消了。
Future接口定义了这些方法:
public interface Future<V> {
// 如果任务已经成功了,或已经取消了,是无法再取消的,会直接返回取消成功(true)
// 如果任务还没有开始进行时,发起取消,是可以取消成功的
// 如果取消时,任务已经在运行了,mayInterruptIfRunning 为 true 的话,就可以打断运行中的线程
boolean cancel(boolean mayInterruptIfRunning);
// 返回线程是否已经被取消了,true 表示已经被取消了
// 如果线程已经运行结束了,isCancelled 和 isDone 返回的都是 true
boolean isCancelled();
// 线程是否已经运行结束了
boolean isDone();
// 等待结果返回
// 如果任务被取消了,抛 CancellationException 异常
// 如果等待过程中被打断了,抛 InterruptedException 异常
V get() throws InterruptedException, ExecutionException;
// 等待,但是带有超时时间的,如果超时时间外仍然没有响应,抛 TimeoutException 异常
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
从接口上可以看出,Future 定义了各种方法对任务进行了管理,比如说取消任务,得到任务的
计算结果等等。
RunnableFuture 也是一个接口,定义如下:
public interface RunnableFuture<V> extends Runnable, Future<V> {
void run();
}
RunnableFuture接口的最大目的,是让Future可以对Runnable进行处理,可以取消Runnable,查看Runnable是否完成等等。
前面提到了Runnable会转化为Callable,然后作为FutureTask的属性,FutureTask统一了 Callable 和 Runnable,提供了两者一系列的转化方法。再来看下FutureTask的类定义
public class FutureTask<V> implements RunnableFuture<V> {
}
FutureTask 实现了 RunnableFuture 接口,也就是说间接实现了Runnnable 接口(RunnableFuture 实现了 Runnnable 接口),就是说 FutureTask 本身就是个 Runnnable,同时 FutureTask 也实现了 Future,也就是说 FutureTask 具备对任务进行管理的功能(Future 具备对任务进行管理的功能)。
private volatile int state;
private static final int NEW = 0; //线程任务创建
private static final int COMPLETING = 1; //任务执行中
private static final int NORMAL = 2; //任务执行结束
private static final int EXCEPTIONAL = 3; //任务异常
private static final int CANCELLED = 4; //任务取消成功
private static final int INTERRUPTING = 5; //任务正在被打断中
private static final int INTERRUPTED = 6; //任务被打断成功
// 组合了 Callable
private Callable<V> callable;
// 异步线程返回的结果
private Object outcome; // non-volatile, protected by state reads/writes
// 当前任务所运行的线程
private volatile Thread runner;
// 记录调用 get 方法时被等待的线程
private volatile WaitNode waiters;
Future两个构造器
//使用 Runnable 初始化,并传入 result 作为返回结果。
// Runnable 是没有返回值的,所以 result 一般没有用,置为 null 就好了
public FutureTask(Runnable runnable, V result) {
// Executors.callable 方法把runnable适配成 RunnableAdapter,RunnableAdapter 实现了Callable
this.callable = Executors.callable(runnable, result);
//任务状态初始化
this.state = NEW; // ensure visibility of callable
}
// 使用 Callable 进行初始化
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
//任务状态初始化
this.state = NEW; // ensure visibility of callable
}
Runnable的两个构造器,只有一个目的,就是通过Executors.callable把入参转化为RunnableAdapter
,主要是因为Callable的功能比Runnable丰富,Callable有返回值,而Runnable没有。
这是一个典型的适配模型,我们要把 Runnable 适配成 Callable,首先要实现 Callable 的接口,接着在 Callable 的 call 方法里面调用被适配对象(Runnable)的方法。
几个关键的方法实现
get有无限阻塞和带超时时间两种方法,一般建议使用带超时时间的方法,源码如下
public V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException {
if (unit == null)
throw new NullPointerException();
int s = state;
//如果任务已经在执行中了,并且等待一定的时间后,仍然在执行中,直接抛出异常
if (s <= COMPLETING &&
(s = awaitDone(true, unit.toNanos(timeout))) <= COMPLETING)
throw new TimeoutException();
//任务执行成功,返回执行的结果
return report(s);
}
// 等待任务执行完成
private int awaitDone(boolean timed, long nanos)
throws InterruptedException {
// The code below is very delicate, to achieve these goals:
// - call nanoTime exactly once for each call to park
// - if nanos <= 0L, return promptly without allocation or nanoTime
// - if nanos == Long.MIN_VALUE, don't underflow
// - if nanos == Long.MAX_VALUE, and nanoTime is non-monotonic
// and we suffer a spurious wakeup, we will do no worse than
// to park-spin for a while
long startTime = 0L; // Special value 0L means not yet parked
WaitNode q = null;
boolean queued = false;
//无限循环
for (;;) {
//当前任务状态
int s = state;
//当前任务已经执行完了,返回
if (s > COMPLETING) {
if (q != null)
q.thread = null;
return s;
}
//如果正在执行,当前线程让出 cpu,重新竞争,防止cpu飙高
else if (s == COMPLETING)
// We may have already promised (via isDone) that we are done
// so never return empty-handed or throw InterruptedException
Thread.yield();
//如果线程已经被打断了,删除,抛异常
else if (Thread.interrupted()) {
removeWaiter(q);
throw new InterruptedException();
}
// 如果第一次运行,新建waitNode,当前线程就是waitNode 的属性
else if (q == null) {
if (timed && nanos <= 0L)
return s;
q = new WaitNode();
}
else if (!queued)
queued = U.compareAndSwapObject(this, WAITERS,
q.next = waiters, q);
else if (timed) {
final long parkNanos;
if (startTime == 0L) {
// first time
startTime = System.nanoTime();
if (startTime == 0L)
startTime = 1L;
parkNanos = nanos;
} else {
long elapsed = System.nanoTime() - startTime;
if (elapsed >= nanos) {
removeWaiter(q);
return state;
}
parkNanos = nanos - elapsed;
}
// nanoTime may be slow; recheck before parking
// 没有过超时时间,线程进入TIMED_WAITING状态
if (state < COMPLETING)
LockSupport.parkNanos(this, parkNanos);
}
else
LockSupport.park(this);
}
}
get 方法虽然名字叫做 get,但却做了很多 wait 的事情,当发现任务还在进行中,没有完成时,就会阻塞当前进程,等待任务完成后再返回结果值。阻塞底层使用的是 LockSupport.park方法,使当前线程进入 WAITING 或TIMED_WAITING 状态。
/**
* run方法可以直接被调用
* 也可以开启新的线程调用
*/
public void run() {
// 状态不是任务创建,或者当前任务已经有线程在执行了,直接返回
if (state != NEW ||
!U.compareAndSwapObject(this, RUNNER, null, Thread.currentThread()))
return;
try {
Callable<V> c = callable;
// Callable 不为空,并且已经初始化完成
if (c != null && state == NEW) {
V result;
boolean ran;
try {
//调用执行
result = c.call();
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex);
}
if (ran)
//将result赋值给outcome
set(result);
}
} finally {
// runner must be non-null until state is settled to
// prevent concurrent calls to run()
runner = null;
// state must be re-read after nulling runner to prevent
// leaked interrupts
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}
1、run方法是没有返回值的,通过给outcome属性赋值(set(result)),get时就能从outcome属性中拿到返回值。
2、FutureTask 两种构造器,最终都转化成了 Callable,所以在 run 方法执行的时候,只需要执行 Callable 的 call 方法即可,在执行 c.call()代码时,如果入参是 Runnable 的话, 调用路径为 c.call() -> RunnableAdapter.call() -> Runnable.run(),如果入参是 Callable 的话,直接调用。
// 取消任务,如果正在运行,尝试去打断
public boolean cancel(boolean mayInterruptIfRunning) {
if (!(state == NEW &&//任务状态不是创建,并且不能把new状态置为取消,直接返回false
U.compareAndSwapInt(this, STATE, NEW,
mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
return false;
try {
// in case call to interrupt throws exception
// 进行取消操作,打断可能会抛出异常,选择 try finally 的结构
if (mayInterruptIfRunning) {
try {
Thread t = runner;
if (t != null)
t.interrupt();
} finally {
// final state
//状态设置成已打断
U.putOrderedInt(this, STATE, INTERRUPTED);
}
}
} finally {
// 清理线程
finishCompletion();
}
return true;
}
1.面试官系统精讲Java源码及大厂真题
完