標籤:executor future futuretask callable
聊聊高並發(三十九)解析java.util.concurrent各個組件(十五) 理解ExecutorService介面的設計這篇說了ExecutorService介面擴充了Executor介面,在執行任務的基礎上,提供了執行架構生命週期的管理,任務的非同步執行,批量任務的執行的能力。AbstractExecutorService抽象類別實現了ExecutorService介面,提供了任務非同步執行和批量執行的預設實現。這篇說說任務的非同步執行和狀態控制
說明一點,使用Executor架構執行任務的方式基本都是非同步執行的,交給線程池的線程來執行。ExecutorService在任務非同步執行的基礎上,通過Future介面來對非同步執行的任務進行狀態控制。
submit方法可以返回Future對象來對非同步執行任務進行控制。submit方法有三種調用方式,傳遞Runnable, Runnable和result,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(Runnable task, T result) { if (task == null) throw new NullPointerException(); RunnableFuture<T> ftask = newTaskFor(task, result); 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; }
從submit的實現可以看到,都是使用了newTaskFor方法進行了介面的適配,返回一個RunnableFuture類型
protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) { return new FutureTask<T>(runnable, value); } protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) { return new FutureTask<T>(callable); }
來看一下對非同步執行的任務進行控制的Future介面相關的類
1. Ruuable表示可被Thread執行的任務,它的run方法沒有返回值,並且沒有顯式的異常
2. Callable表示可以調用的任務,它的call方法可以有返回值,可以拋出顯式的異常
3. Future介面是對非同步執行的任務的控制,包括取消任務,判斷任務狀態,擷取任務結果等操作
4. RunnableFuture是對Runnable介面和Future介面的適配,表示可以被控制狀態的Runnable
5. RunnableAdapter是對Runnable和Callalbe的適配,實現了Callable介面,並彙總了Runnable對象
6. FutureTask實現了RunnableFuture介面,通過RunnableAdapter對傳入的Callable和Runnable參數進行統一處理
public interface Runnable { public abstract void run();}public interface Callable<V> { V call() throws Exception;}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; } }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;}public interface RunnableFuture<V> extends Runnable, Future<V> { void run();}
在ExecutorService的submit方法中可以看到最終被執行的任務是封裝成了RunnableFuture類型,它既是Runnable可以被Executor執行,又是Future,可以控制非同步執行的Runnable的狀態。
重點來看一下RunnableFuture的實作類別FutureTask,非同步執行的任務的狀態控制都是在這個類中實現的。
FutureTask的主要屬性
1. private volatile int state; volatile類型的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;
2. private Callable<V> callable; 可以獲得計算結果的callable對象,封裝了傳入的任務
3. private Object outcome; 任務執行的結果,可以是正常計算得到的結果,也可以是要返回的異常
4. private volatile Thread runner; 執行任務的線程
5. private volatile WaitNode waiters; 等待的線程鏈表
FutureTask對state, runner, waiters這3個需要原子操作的對象,沒有使用AtomicXXX原子變數,而是使用了Unsafe對象來直接操作記憶體進行原子操作。關於Unsafe對象請參考這篇聊聊高並發(十七)解析java.util.concurrent各個組件(一) 瞭解sun.misc.Unsafe類
// Unsafe mechanics private static final sun.misc.Unsafe UNSAFE; private static final long stateOffset; private static final long runnerOffset; private static final long waitersOffset; static { try { UNSAFE = sun.misc.Unsafe.getUnsafe(); Class<?> k = FutureTask.class; stateOffset = UNSAFE.objectFieldOffset (k.getDeclaredField("state")); runnerOffset = UNSAFE.objectFieldOffset (k.getDeclaredField("runner")); waitersOffset = UNSAFE.objectFieldOffset (k.getDeclaredField("waiters")); } catch (Exception e) { throw new Error(e); } }
來看看FutureTask的主要方法
建構函式支援Callable類型和Runnable類型,當使用Runnable類型的參數時,需要傳入result作為Callable的計算結果,利用RunnableAdapter進行從Runnable到Callable的適配。FutureTask的初始狀態是NEW
public FutureTask(Callable<V> 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.callable() public static <T> Callable<T> callable(Runnable task, T result) { if (task == null) throw new NullPointerException(); return new RunnableAdapter<T>(task, result); }
run方法會被Executor的背景工作執行緒調用,因為FutureTask是作為Runnable對象傳遞給Executor的execute方法的。
1. 把當前線程設定成FutureTask的runner。
2. 執行傳入的Callable的call方法,並把結果傳給result對象。如果call方法產生異常,就調用setException方法把異常對象傳遞給outcome屬性,並設定FutureTask的相應狀態,先設定成COMPLETING,再設定成EXCEPTIONAL
3. 如果call方法正常執行完成,調用set()方法把結果傳遞給outcome屬性,並設定FutureTask的相應狀態,先設定成COMPLETING,再設定成NORMAL
4. 調用finishCompletion方法來喚醒在get()方法中阻塞的線程
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 setException(Throwable t) { if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) { outcome = t; UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final state finishCompletion(); } }protected void set(V v) { if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) { outcome = v; UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state finishCompletion(); } }
cancel方法會取消執行的任務。當state為NEW的時候才能被取消。如果取消任務時要中斷任務,會先把FutureTask狀態設定為INTERRUPTING,然後調用執行任務的線程的interrupt()方法去中斷任務,然後把狀態設定成INTERRUPTED。如果取消任務時不中斷任務,直接把FutureTask狀態設定成CANCELLED。最後調用finishCompletiong方法來喚醒和刪除在get方法中等待的線程
public boolean cancel(boolean mayInterruptIfRunning) { if (state != NEW) return false; if (mayInterruptIfRunning) { if (!UNSAFE.compareAndSwapInt(this, stateOffset, NEW, INTERRUPTING)) return false; Thread t = runner; if (t != null) t.interrupt(); UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED); // final state } else if (!UNSAFE.compareAndSwapInt(this, stateOffset, NEW, CANCELLED)) return false; finishCompletion(); return true; } private void finishCompletion() { // assert state > COMPLETING; for (WaitNode q; (q = waiters) != null;) { if (UNSAFE.compareAndSwapObject(this, waitersOffset, q, null)) { for (;;) { Thread t = q.thread; if (t != null) { q.thread = null; 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 }// 鉤子方法protected void done() { }
get()和get(long timeout, TimeUnit unit)方法會嘗試擷取FutureTask的運行結果。前者會一直等待,直到FutureTask的狀態大於COMPLETING,即call執行結束,然後report()方法返回結果。後者會等待timeout時間,如果timeout就拋出TimeoutException,否則report()方法返回結果
public V get() throws InterruptedException, ExecutionException { int s = state; if (s <= COMPLETING) s = awaitDone(false, 0L); return report(s); } /** * @throws CancellationException {@inheritDoc} */ 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); }
awaitDone方法利用了LockSupport對象來實現條件隊列操作,LockSupport.park() LockSupport.unpark()方法相當於即Object.wait(),Object.notify()方法。
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); } }
report方法處理FutureTask的返回值,如果正常執行,就返回正常結果,如果是取消或者中斷,就拋出CancellationException,如果執行過程中發生了異常,就用ExecutionException來封裝拋出的異常,並拋出ExecutionException
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); }
提交任務的生產者線程和執行任務的消費者背景工作執行緒共用FutureTask對象,這兩個線程之間可以通過FutureTask的狀態相互作用
著作權聲明:本文為博主原創文章,未經博主允許不得轉載。
聊聊高並發(四十一)解析java.util.concurrent各個組件(十七) 任務的非同步執行和狀態控制