共计 4790 个字符,预计需要花费 12 分钟才能阅读完成。
摘要
- 什么是 Future
- 为什么须要 Future
- Java 中的 Future 模式
- 详解 FutureTask
1. 什么是 Future
Future 是多线程开发中常见的一种设计模式。Future 模式能够返回线程执行后果的契约,通过此契约程序能够抉择在适合的机会取回执行的后果,如果取回后果时线程还没有执行实现,将会阻塞调用线程期待执行后果返回。
2. 为什么须要 Future
在有些场景下,咱们想应用另一个线程去执行简单耗时的操作,此时又不想让主线程期待白白浪费 CPU,此时能够让主线程先去做别的事,而后在适合的机会去通过 Future 契约取回线程执行的后果。
3. Java 中的 Future 模式
Java 中的 Future 模式次要由以上接口和类组成。
3.1 Callable & Runnable
这是咱们一般的线程工作,其中 Callable 是带返回值(实在数据),Runnable 是不带返回值的,因而在咱们应用 Runnable 和 Future 时,必须传入一个 Result 对象,通过 Future 在获取后果时就是获取的该 Result,外围代码如下:
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;
}
}
Callable 目前只能搭配线程池或者 Future 来应用,不能间接和 new Thread() 搭配应用,Runnable 能够搭配线程池和 new Thread() 应用,在配合 Future 应用时实质上是对其进行了适配,也就是上述代码中的 RunnableAdapter。
3.2 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;
}
Future 是线程的契约,通过其 get() 办法咱们能够获取线程执行的后果,当然 Future 也提供了其余三个办法,别离是:
- cancel:勾销工作
- isCancelled:工作是否曾经勾销
- isDone:工作是否实现
3.3 RunnableFuture
public interface RunnableFuture<V> extends Runnable, Future<V> {void run();
}
RunnableFuture 接口继承自 Runnable 和 Future,表明 RunnableFuture 能够被线程执行并且能够通过契约获取到线程的执行后果。
4. FutureTask
4.1 属性
// 执行工作
private Callable<V> callable;
// 工作的理论执行后果
private Object outcome;
// 执行工作的线程
private volatile Thread runner;
// 期待后果的线程栈
private volatile WaitNode waiters;
4.2 状态
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;
FutureTask 除了 4.1 中的属性外,还有一个重要的属性就是 state,FutureTask 中的状态大概有 7 种:
- NEW:工作的初始状态
- COMPLETING:正在设置工作后果
- NORMAL:工作执行结束
- EXCEPTIONAL:工作发行异样
- CANCELLED:工作被勾销
- INTERRUPTING:正在中断工作
- INTERRUPTED:工作被中断
4.3 run() 办法
工作执行的时候理论就是执行 run 办法,源码如下:
public void run() {
if (state != NEW ||
!UNSAFE.compareAndSwapObject(this, runnerOffset,
null, Thread.currentThread()))
return;
try {
Callable<V> c = 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)
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);
}
}
protected void set(V v) {if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
outcome = v;
UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
finishCompletion();}
}
private void handlePossibleCancellationInterrupt(int s) {
// It is possible for our interrupter to stall before getting a
// chance to interrupt us. Let's spin-wait patiently.
if (s == INTERRUPTING)
while (state == INTERRUPTING)
Thread.yield(); // wait out pending interrupt}
run 办法的大抵流程如下:
- 校验工作的状态是否是 NEW 和以后是否无执行线程,如果校验通过,则获取工作执行
- 调用工作的 call 办法
- 如果执行异样,设置后果,状态批改为 EXCEPTIONAL,并将工作后果设置为异样
- 如果失常执行,调用 set(V v) 设置后果,状态批改为 NORMAL,后果设置为执行后果,并且唤醒期待后果的线程
- 最初在 finally 块中,咱们将 runner 属性置为 null,并且查看有没有脱漏的中断,如果发现 s >= INTERRUPTING, 阐明执行工作的线程有可能被中断了,因为 s >= INTERRUPTING 只有两种可能,state 状态为 INTERRUPTING 和 INTERRUPTED。
4.3 get() 办法
当咱们须要去获取 FutureTask 的后果时,咱们须要调用 get 办法获取后果。
public V get() throws InterruptedException, ExecutionException {
int s = state;
if (s <= COMPLETING)
s = awaitDone(false, 0L);
return report(s);
}
@SuppressWarnings("unchecked")
private V report(int s) throws ExecutionException {
Object x = outcome;
if (s == NORMAL)
return (V)x;
if (s >= CANCELLED)
throw new CancellationException();
throw new ExecutionException((Throwable)x);
}
private int awaitDone(boolean timed, long nanos)
throws InterruptedException {final long deadline = timed ? System.nanoTime() + nanos : 0L;
WaitNode q = null;
boolean queued = false;
for (;;) {if (Thread.interrupted()) {removeWaiter(q);
throw new InterruptedException();}
int s = state;
if (s > COMPLETING) {if (q != null)
q.thread = null;
return s;
}
else if (s == COMPLETING) // cannot time out yet
Thread.yield();
else if (q == null)
q = new WaitNode();
else if (!queued)
queued = UNSAFE.compareAndSwapObject(this, waitersOffset,
q.next = waiters, q);
else if (timed) {nanos = deadline - System.nanoTime();
if (nanos <= 0L) {removeWaiter(q);
return state;
}
LockSupport.parkNanos(this, nanos);
}
else
LockSupport.park(this);
}
}
获取后果的大抵步骤如下:
- 检测工作状态是否是 NEW 或者 COMPLETING,如果不是,阐明曾经执行胜利或失败,返回后果
- 否则就阻塞期待,阻塞期待的步骤如下
- 检测以后线程是否被中断,如果是就将其从期待线程中移除
- 再次检测工作状态,如果是异样、中断或者执行实现状态,则间接返回后果。
- 如果工作是 COMPLETING 状态,阐明工作曾经执行实现正在设置后果,此时让获取后果的线程短暂让出 CPU 持续期待
- 如果期待后果的线程栈为 null,阐明还没有生成,则生成期待后果的线程栈
- 如果 queued 为 false,阐明期待后果的线程还没入栈,所以将其入栈
- 最初看是否是是超时期待,依据是否超时,抉择将期待后果的线程永恒挂起(期待唤醒)还是具备超时工夫的挂起
本期的 Java Future 就介绍到这,我是 shysh95,咱们下期再见!