使用场景
当需要获取另一个线程的的执行结果的时候,可以用Future + callable结合来使用。
获取线程执行结果使用案例
public class FutureTaskDemo implements Callable {
@Override
public String call() throws Exception {
System.out.println("执行 callable");
TimeUnit.SECONDS.sleep(2);
return "callable";
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
//1. 定义任务
Callable callable = new FutureTaskDemo();
//2. 定义一个可以取消的异步计算过程
FutureTask future = new FutureTask<>(callable);
//3. 通过线程执行异步计算过程
new Thread(future).start();
//4. 获取返回结果
try {
String result = future.get();
System.out.println(result);
} catch (ExecutionException | CancellationException e ) {
System.out.println("任务被取消或者抛出了异常");
}catch (InterruptedException e){
//当前线程被中断了
System.out.println("当前线程被中断了");
}
}
}
Future&Callable源码流程图
Callable 与 Runable区别
Callable 一个能够返回结果的任务,只要一个没有参数的方法v call()。
Runable 只有一个run()方法,能被线程执行。
2个方法类似,不过一个有返回值一个没有返回值。 Runable没有抛出异常,Callable抛出了异常,需要自己处理。
一个线程执行了一个任务,因为线程的run()方法是没有返回结果的,所以我们要想得到返回结果需要在定义一个接口并实现这个接口。我们要想异步执行且获取执行的返回结果,这里有2个需求 1.通过线程执行 2. 获取返回数据。
看一段伪代码。
public class Test implements Runnable {
private String item;
public static void main(String[] args) {
Test test = new Test();
//线程执行
new Thread(test).run();
//2 获取返回结果
System.out.println(test.getItem());
}
private String getItem(){
return item;
}
@Override
public void run() {
item = "1";
}
}
以上就是通过简单的伪代码来实现异步线程执行计算并获取计算的结果。那么在java中是通过一个RunableFuture 接口定义这种行为。
RunableFuture
一个接口继承了 Runable 接口与 Future接口。RunableFuture extends Runnable, Future
FutureTask
RunableFuture接口的实现类。线程执行任务并获取任务的返回结果。这个类的方法可以开启和停止计算。get方法将阻塞知道计算完成。
FutureTask的使用看获取线程执行结果使用案例。他有2个构造方法!
- FutureTask(Callable
) 入参是Callable。
public FutureTask(Callable callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
public FutureTask(Runnable runnable, V result) { this.callable = Executors.callable(runnable, result); this.state = NEW; // ensure visibility of callable } //Executors public static
Callable callable(Runnable task, T result) { if (task == null) throw new NullPointerException(); return new RunnableAdapter (task, result); } //适配器模式 对Runable的封装。 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; } }
2个构造函数入参不管是Runable 还是 Callable最终都会适配成Callable接口。并赋值给类的属性。
private Callable callable;
FutureTask源码
FutrueTask肯定会涉及到多线程问题,包括线程的中断与唤醒。多线程问题肯定会有一个变量作为临界值,通过cas去更改这个临界值来实现多线程环境下的业务逻辑。FutureTask中通过 state 作为临界属性。下面是state中几种类型的变化。
* 状态的几种变化
* 新建 完成 正常
* NEW -> COMPLETING -> NORMAL 正常执行完成
* 新建 完成 抛出异常 call() 抛出异常
* NEW -> COMPLETING -> EXCEPTIONAL
* 新建 取消 cancel(false)
* NEW -> CANCELLED
* 新建 中断中 中断完成 cancel(true)
* NEW -> INTERRUPTING -> INTERRUPTED
*/
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;
在案例中的第三步骤,//3. 通过线程执行异步计算过程。把FutureTask交给了线程去执行。我们知道线程一定会执行Runable的run方法,而FutureTask是RunableFuture的实现类,RunableFuture继承Runable接口。所以FutureTask一定会有一个实现了Runable接口的run()方法。且线程执行的时候一定会执行这个方法。
public void run() {
//cas 设置属性 runner 为当前线程
if (state != NEW ||
!UNSAFE.compareAndSwapObject(this, runnerOffset,
null, Thread.currentThread()))
return;
try {
Callable c = callable;
//FutureTask 构造函数中state=NEW代表状态是初始值。
if (c != null && state == NEW) {
V result;
boolean ran;
try {
//1. 执行Callable的call方法 callable是内部属性,由构造方法传入。
result = c.call();
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex);
}
//如果执行完
if (ran)
//2.设置返回结果
set(result);
}
} finally {
runner = null;
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}
这里的逻辑比较简单,就是执行内部的属性Callable接口的call方法。执行完成之后,步骤2设置返回值
protected void set(V v) {
//1. 通过cas设置state状态
if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
//2. 属性outcome 指向返回值
outcome = v;
//3. 设置state为 NORMAL(正常)
UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
finishCompletion();
}
}
private void finishCompletion() {
//1. 循环获取 WaitNode
for (WaitNode q; (q = waiters) != null;) {
//2. cas设置WaitNode = null
if (UNSAFE.compareAndSwapObject(this, waitersOffset, q, null)) {
//3. 自旋单项链表,一个个的唤醒Thread
for (;;) {
Thread t = q.thread;
if (t != null) {
q.thread = null;
//4 唤醒
LockSupport.unpark(t);
}
WaitNode next = q.next;
if (next == null)
break;
q.next = null; // unlink to help gc
q = next;
}
break;
}
}
done();
callable = null; // to reduce footprint
}
这里我有2个疑问步骤1通过cas设置state状态,因为state是用 volatitle修饰的索引修改对所有内存有效,如果不是呢?是否还对所有内存有效。换句话说cas修改的是主内存还是高速缓存。第2个问题。 cas没有自旋的操作,也就是说加入compareAndSwapInt失败,直接返回false,state状态就不会是完成时了 第3个问题 步骤3 的设置为什么不用 cas呢?。
如果这个时候call()被阻塞了。而主线程执行future.get();方法会阻塞直到被上面的步骤4唤醒。
public V get() throws InterruptedException, ExecutionException {
int s = state;
if (s <= COMPLETING)
//等待
s = awaitDone(false, 0L);
return report(s);
}
private int awaitDone(boolean timed, long nanos)
throws InterruptedException {
//1. 判断是否设置了超时时间
final long deadline = timed ? System.nanoTime() + nanos : 0L;
WaitNode q = null;
boolean queued = false;
for (;;) {
//当前线程处于中断状态的情况下,返回true且线程复位。
if (Thread.interrupted()) {
//移除当前节点
removeWaiter(q);
//抛出异常
throw new InterruptedException();
}
int s = state;
//2. 结束自旋条件 业务执行结束或者已取消
if (s > COMPLETING) {
if (q != null)
q.thread = null;
return s;
}
else if (s == COMPLETING) // cannot time out yet
//告诉cup参与竞争
Thread.yield();
else if (q == null)
q = new WaitNode();
else if (!queued)
//3. cas设置 属性waiters。相当于一个单项链表
queued = UNSAFE.compareAndSwapObject(this, waitersOffset,
q.next = waiters, q);
else if (timed) {
nanos = deadline - System.nanoTime();
if (nanos <= 0L) {
//移除当前节点
removeWaiter(q);
return state;
}
//6. 当前线程阻塞nanos秒
LockSupport.parkNanos(this, nanos);
}
else
//5. 阻塞当前线程
LockSupport.park(this);
}
}
cancel(boolean flag) 取消或者中断执行的任务
public boolean cancel(boolean mayInterruptIfRunning) {
//任务执行完不可以取消 cas设置state状态为 中断或者取消
if (!(state == NEW &&
UNSAFE.compareAndSwapInt(this, stateOffset, NEW,
mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
return false;
try { // in case call to interrupt throws exception
if (mayInterruptIfRunning) {
try {
Thread t = runner;
if (t != null)
//如果入参是ture 则发起中断标识
t.interrupt();
} finally { // final state
UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED);
}
}
} finally {
//通知node链表,唤醒等待线程
finishCompletion();
}
return true;
}