摘要
- 什么是Future
- 为什么须要Future
- Java中的Future模式
- 详解FutureTask
1. 什么是Future
Future是多线程开发中常见的一种设计模式。Future模式能够返回线程执行后果的契约,通过此契约程序能够抉择在适合的机会取回执行的后果,如果取回后果时线程还没有执行实现,将会阻塞调用线程期待执行后果返回。
2. 为什么须要Future
在有些场景下,咱们想应用另一个线程去执行简单耗时的操作,此时又不想让主线程期待白白浪费CPU,此时能够让主线程先去做别的事,而后在适合的机会去通过Future契约取回线程执行的后果。
3. Java中的Future模式
Java中的Future模式次要由以上接口和类组成。
3.1 Callable & Runnable
这是咱们一般的线程工作,其中Callable是带返回来源gao($daima.com搞@代@#码网值(实在数据),Runnable是不带返回值的,因而在咱们应用Runnable和Future时,必须传入一个Result对象,通过Future在获取后果时就是获取的该Result,外围代码如下:
<code class="java">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
<code class="java">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
<code class="java">public interface RunnableFuture<V> extends Runnable, Future<V> { void run(); }
RunnableFuture接口继承自Runnable和Future,表明RunnableFuture能够被线程执行并且能够通过契约获取到线程的执行后果。
4. FutureTask
4.1 属性
<code class="java">// 执行工作 private Callable<V> callable; // 工作的理论执行后果 private Object outcome; // 执行工作的线程 private volatile Thread runner; // 期待后果的线程栈 private volatile WaitNode waiters;
4.2 状态
<code class="java">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办法,源码如下:
<code class="java">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办法获取后果。
<code class="java"> 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,咱们下期再见!