通过直接继承Thread, 实现Runnable接口来创建线程。但这两种方式都有一种缺陷:在执行完任务之后无法获得执行结果。
如果需要获得执行结果,就必须通过共享变量或者使用线程通信的方式来达到效果,这样使用起来比较麻烦,而jdk中Callable和Future,通过他们可以在任务执行完毕之后得到任务执行结果。先看看他们之间的组织关系:
Callable:
public interface Callable<V> { /** * Computes a result, or throws an exception if unable to do so. * * @return computed result * @throws Exception if unable to compute a result */ V call() throws Exception; }
源码可知,它也是个一个接口,在他里面也只是申明一个方法,只不过这个方法为call(),call方法返回的就是该泛型传递进来的V类型,他怎么使用呢?就是结合之前的ExecuteService:
<T> Future<T> submit(Callable<T> task); <T> Future<T> submit(Runnable task, T result); Future<?> submit(Runnable task);
第一个submit方法里面的参数类型就是Callable。
Future:
Future就是对于具体的Runnable或者Callable任务的执行进度的查看,取消,查询是否完成,获取结果(正确完成时的结果,或异常)。必要时可以通过get方法获取执行的结果,该方法会阻塞直到任务返回结果,或通过指定阻塞时间的版本。
public interface Future<V> { boolean cancel(boolean mayInterruptIfRunning); boolean isDone(); V get() throws InterruptedException, ExecutionException; V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException; }
其中cancel()方法用来取消任务,如果取消任务成功则返回true, 如果取消任务失败则返回false。 参数mayInterruptIfRunning表示是否允许取消真在执行去没有执行完毕的任务,如果设置true, 则表示可以取消正在执行过程的任务。 当任务已经完成,或者已经被取消过了,或者因为别的原因不能取消, 则返回false。 当取消时,该任务还没有开始执行,则该任务不会执行,并且总是返回true。
FutureTask:
public class FutureTask<V> implements RunnableFuture<V>
FutureTask类实现了RunnableFuture接口,看一下RunnableFuture接口的定义:
public interface RunnableFuture<V> extends Runnable, Future<V>
RunnableFuture接口接触了Runnable接口和Future接口, 而FutureTask实现了RunnableFuture接口,所以它既可作为Runnable被线程执行,也可以作为Future得到Callable的返回值。
构造器定义:
public FutureTask(Callable<V> callable) public FutureTask(Runnable runnable, V result) {
再来看看第二个构造器中的参数怎么变身Callable的:
this.callable = Executors.callable(runnable, result);
调用Executors.callable方法:
public static <T> Callable<T> callable(Runnable task, T result) { if (task == null) throw new NullPointerException(); return new RunnableAdapter<T>(task, result); }
简单实现Callable:
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; } }
流程:
下面结合完整具体流程走一下FutureTask过程,并解析源码,草图如下:
实例代码如下:
1 public class Test { 2 public static void main(String[] args) { 3 //第一种方式 4 ExecutorService executor = Executors.newCachedThreadPool(); 5 Task task = new Task(); 6 FutureTask<Integer> futureTask = new FutureTask<Integer>(task); 7 executor.submit(futureTask); 8 executor.shutdown(); 15 16 try { 17 Thread.sleep(1000); 18 } catch (InterruptedException e1) { 19 e1.printStackTrace(); 20 } 21 22 System.out.println("主线程在执行任务"); 23 24 try { 25 System.out.println("task运行结果"+futureTask.get()); 26 } catch (InterruptedException e) { 27 e.printStackTrace(); 28 } catch (ExecutionException e) { 29 e.printStackTrace(); 30 } 31 32 System.out.println("所有任务执行完毕"); 33 } 34 } 35 class Task implements Callable<Integer>{ 36 @Override 37 public Integer call() throws Exception { 38 System.out.println("子线程在进行计算"); 39 Thread.sleep(3000); 40 int sum = 0; 41 for(int i=0;i<100;i++) 42 sum += i; 43 return sum; 44 } 45 }
分析过程之前,先准备前准备知识,首先看一下FutureTask内部状态,以及之间的转变:
private volatile int state; // volatile 内存可见性 private static final int NEW = 0; //该状态为new FutureTask()时设定,同时也表示内部成员callable已经成功赋值,一直到worker thread完成FutureTask中run(). private static final int COMPLETING = 1; //该状态位worker thread完成task时设定的中间状态,处于该状态下,说明worker thread 真正准备设置result. private static final int NORMAL = 2; //当设置result结果完成后,FutureTask处于该状态,代表过程结果,该状态为最终状态final state,(正确完成的最终状态) private static final int EXCEPTIONAL = 3; // 同上,只不过task执行过程出现异常,此时结果设值为exception,也是final state private static final int CANCELLED = 4; //final state, 表明task被cancel(task还没有执行就被cancel的状态). private static final int INTERRUPTING = 5; // 中间状态,task运行过程中被interrupt时,设置的中间状态; private static final int INTERRUPTED = 6; // final state, 中断完毕的最终状态,几种情况,下面具体分析。
下面是状态之间的转变,贯穿主线:
* Possible state transitions: 1* NEW -> COMPLETING -> NORMAL 2* NEW -> COMPLETING -> EXCEPTIONAL 3* NEW -> CANCELLED 4* NEW -> INTERRUPTING -> INTERRUPTED */
其他重要的变量:
/** The underlying callable; nulled out after running */ private Callable<V> callable; // 具体run运行时会调用其方法call(),并获得结果,结果时置为null. /** The result to return or exception to throw from get() */ private Object outcome; // non-volatile, protected by state reads/writes 没必要为votaile,因为其是伴随state 进行读写,而state是FutureTask的主导因素。 /** The thread running the callable; CASed during run() */ private volatile Thread runner; //具体的worker thread. /** Treiber stack of waiting threads */ private volatile WaitNode waiters; //Treiber stack 并发stack数据结构,用于存放阻塞在该futuretask#get方法的线程。
OK,构造new FutureTask开始:
public FutureTask(Callable<V> callable) { if (callable == null) throw new NullPointerException(); this.callable = callable; //底层callable赋值 this.state = NEW; // 初始状态NEW,同时也标志了callable的赋值,可见性 }
ThreadPoolExecutor.submit(Runnable),ThreadPoolExecutor里面具体细节请见这里,这里就假设它直接new a thread来处理该任务了,因为FutureTask为Runnable的子类,所以worker thread调用该类的run()方法:
public void run() {
if (state != NEW || !UNSAFE.compareAndSwapObject(this, runnerOffset, null, Thread.currentThread())) //状态检测,和当前worker Thread的cas原子赋值,有一个不成立,就直接返回。什么情况下还没run()呢?就不是NEW状态了呢? return; //caller调用cancel了,此时状态为Interrupting,也说明了上面的cancel方法说明,task没运行时,就interrupt,task得不到运行。总是返回 try { //true。
//再来看看这里worker thread赋值为什么要用cas操作,有竞争racing? 竞争哪里来?难道threadPoolExecutor线程池多个线程可能抢同一个 Callable<V> c = callable; //任务?不可能 1:线程数 < coreThreadPool 时, 直接new thread, 2 : 大于 coreThreadpool时,放在blockingqueue里,取的话只能一 if (c != null && state == NEW) { //线程。能想到就是caller那边了,即多callers(多线程)提交同一FutureTask. V result; //多线程同时提交同一FutureTask,确保该FutureTask的run()只被调用一次, boolean ran; try { result = c.call(); //此处的if,1:当state == NEW(task没完成,中断) 并且 worker Thread为null时,才会得到运行 ran = true; // 2: task已经完成了 或者 该任务已经有worker thread来执行时,直接返回不会运行。 } catch (Throwable ex) { //调用callable的call方法 result = null; //执行task时有异常 ran = false; //附异常 setException(ex); } if (ran) //正常完成,则赋值 set(result); } } finally { //注意!!什么这里吧runner置为null,此时run()方法还没运行完呢啊!现在置为null,不怕并发调用run()吗?注意此时state已经变化了(Comple runner = null; //teing或者interrupting了,run()一开始state != NEW 直接return,不会运行。可以说通过state和 worker thread来一起控制并发调用run int s = state; //必须再读一次,防止worker thread == null后,遗漏的interrup信号,底下具体分析中断的情况。 if (s >= INTERRUPTING) handlePossibleCancellationInterrupt(s); //如果caller中断信号有的话,则处理该interrupt. } //另外该任务是一致性任务,即state只要不为NEW,该任务就不会在运行,运行结束或cancel后,就不能在运行了,因为state状态在那不变哦! }
请看下例子,三个提交线程(提交同一个FutureTask):
public class Test { public static void main(String[] args) { ExecutorService executor = Executors.newCachedThreadPool(); Task task = new Task(); CountDownLatch latch = new CountDownLatch(1); FutureTask<Integer> futureTask = new FutureTask<Integer>(task); for (int i = 0 ; i < 3; i++) { new Thread(new Submit(executor, futureTask, latch)).start(); } try { Thread.sleep(3000); latch.countDown(); Thread.sleep(20000); } catch (InterruptedException e1) { e1.printStackTrace(); } System.out.println("所有任务执行完毕"); executor.shutdown(); } } class Submit implements Runnable { private CountDownLatch latch ; private ExecutorService es ; private FutureTask<Integer> task; public Submit(ExecutorService es, FutureTask<Integer> task, CountDownLatch latch) { this.latch = latch; this.es = es; this.task = task; } public void run() { try { latch.await(); Future<?> future = (Future<?>) es.submit(task); System.out.println("Thread name : " + Thread.currentThread().getName() + "go!"); future.get(3000, TimeUnit.MILLISECONDS); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e1) { e1.printStackTrace(); } catch (TimeoutException e2) { System.err.println("Thread name : " + Thread.currentThread().getName() + " " + e2); } } } class Task implements Callable<Integer>{ public Integer call() throws Exception { System.out.println("thread name : " + Thread.currentThread().getName() + "do the work!"); Thread.sleep(6000); int sum = 0; for(int i=0;i<100;i++) sum += i; return sum; } }
显示如下:
Thread name : Thread-1go! Thread name : Thread-0go! Thread name : Thread-2go! thread name : pool-1-thread-1do the work! Thread name : Thread-1 java.util.concurrent.TimeoutException 所有任务执行完毕
结果很显然,同一个任务多次提交(并发提交),FutureTask保证只是启一个线程来运行。
想运行多次(只要不cancel,和throw exception,因为他set(result),正常运行结束,state还是new),用这个:
protected boolean runAndReset() { if (state != NEW || !UNSAFE.compareAndSwapObject(this, runnerOffset, null, Thread.currentThread())) return false; boolean ran = false; int s = state; try { Callable<V> c = callable; if (c != null && s == NEW) { try { c.call(); // don't set result ran = true; } catch (Throwable ex) { setException(ex); } } } finally { runner = null; s = state; if (s >= INTERRUPTING) handlePossibleCancellationInterrupt(s); } return ran && s == NEW; }
再来看看setException()和set(result):
protected void set(V v) { if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) { // cas原子操作,失败直接返回,成功的前提之前的状态必须为NEW. outcome = v; //可能和什么冲突呢? caller已经cancel该task,状态位Interrupting或者Interrpted(这次Interrupted代表interrupt完成,这set() UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state // 不是在worker thread中调用的嘛,怎么intterupt都完成了,怎么worker thread还在运行呢?worker thread运行的代码中没有响 finishCompletion(); //应interrupt的代码。所以客户端cancel操作,对运行中的worker thread,并不一定让它停下来,不过此时即使运行完毕,也不能赋值。 } } //new -> Completing-> NORMAL 或者NEW ->Interrupting->Intterrpted.
protected void setException(Throwable t) { if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) { outcome = t; //同上,不过附异常。 UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final state finishCompletion(); } //new ->completing ->exception 或者 同上 }
finishCompletion()等会细聊,主要是没说到get()阻塞呢!看看caller端线程调用cancel()和workerThread的handlePossibleCancellationInterrupt(int s)协调:
public boolean cancel(boolean mayInterruptIfRunning) { if (state != NEW) return false; //1:已经cancel(cancelled,Interruping, Interrupted)过了 2:正常完成 Completing(Completed) 3:异常完成completing(exception) 直接返回false; if (mayInterruptIfRunning) { // flag : worker thread 已经启动运行了,是否可以中断 if (!UNSAFE.compareAndSwapInt(this, stateOffset, NEW, INTERRUPTING)) //再次检查state状态,完成的话(上面的三种),直接返回false; return false; Thread t = runner; if (t != null) // t == null对应Future task还没启动, 跳过thread.interrupt(),直接由interrpting -> interrupted,成功的话 t.interrupt(); //调用worker thread的 interrupt() //mayInterrptIfRunning 为true ,interrupt 状态转变 new -> interrupting -> interrupted. UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED); // final state } else if (!UNSAFE.compareAndSwapInt(this, stateOffset, NEW, CANCELLED)) //mayInterruptIfRunning 为false,interrupt成功的 状态转变 new -> Cancelled return false; finishCompletion(); return true; }
由上面可知,客户端cancel()中不少cas操作,主要来自两方面的racing, 1:线程池worker Thread的完成(异常,正常)状态设置; 2:同一futuretask,不同客户端线程callers的cancel操作。
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 // assert state == INTERRUPTED; // We want to clear any interrupt we may have received from // cancel(true). However, it is permissible to use interrupts // as an independent mechanism for a task to communicate with // its caller, and there is no way to clear only the // cancellation interrupt. // // Thread.interrupted(); }
当state处于Interrupting, 即caller即将调用worker thread.interrupt(), 所以worker thread自旋会,等会interrupt方法的调用,保留interrupt标志。
再来看看get()和带参数的get(timeout):
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) //等到timeout时间内,没完成,throws TimeoutException throw new TimeoutException(); return report(s); }
awaitDone():
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()) { //阻塞该caller线程之前,caller线程被中断,直接throw 异常 removeWaiter(q); //在阻塞队列中移除该线程的封装node.此处无意义 throw new InterruptedException(); } int s = state; //读取state,阻塞前 recheck一下 是否完成? 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); //设为当前FutureTask阻塞链表(stack结构)的栈顶。 else if (timed) { nanos = deadline - System.nanoTime(); //计算当前要阻塞的等待时间 if (nanos <= 0L) { removeWaiter(q); //小于0 直接返回,当前REMOVEWaiter无意义,并没有加入stack中。 return state; } LockSupport.parkNanos(this, nanos);本地native方法,阻塞当前线程。 } else LockSupport.park(this); //无时间条件阻塞 } }
无时间限制阻塞,有时间阻塞(阻塞时间大于task完成时间)会等到任务完成而给通知,唤醒该线程,即finishCompletion();而有时间阻塞(阻塞时间在task完成之间就已经结束的)会通过for()退出(退出前,删除等待队列中的节点)。
WaiterNode定义:
static final class WaitNode { volatile Thread thread; volatile WaitNode next; WaitNode() { thread = Thread.currentThread(); } //当前阻塞线程的引用 }
结合awaitDone()中的新阻塞节点加入顺序,其定位stack结构(Treiber stack);
removeWaiter():
private void removeWaiter(WaitNode node) { if (node != null) { node.thread = null; retry: for (;;) { // restart on removeWaiter race for (WaitNode pred = null, q = waiters, s; q != null; q = s) { s = q.next; if (q.thread != null) pred = q; else if (pred != null) { pred.next = s; if (pred.thread == null) // 检测竞争 continue retry; //发生重试 } else if (!UNSAFE.compareAndSwapObject(this, waitersOffset, q, s)) continue retry; } break; } } }
finishCompletion():
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 }
其实,前面的分析可知,多个caller线程并发提交同一个FutureTask, 并且所谓调用get()阻塞的话(阻塞在该FutureTask上),实际上也就一个caller线程阻塞,其他线程在调用该FutureTask的run()开始条件检查时,就直接return了,实际情况:三个并发线程提交同一个future task,对应生成三份FutureTask(不同于之前),三份FutureTask中对应三分Callable,而这三份Callable含有相同的FutureTask(所谓的相同任务) ,向ThreadPoolExecutor.submit(Runnable)实际上提交了三份Runnable(即生成的三分FutureTask), FutureTask实现了Runnable接口, 然后ThreadPoolExecutor生成三个线程来执行这所谓的三个任务,这三个任务run()中都是调用对应内部的callable的call(), 而callable的call方法调用的是他们共同引用的FutureTask(同一个对像)的run()方法,而run方法, 我们上面解析过了,通过cas和状态检测,只运行一个worker thread 调用run()(见上),另外两个线程直接从共同底层FutureTask的run方法开始直接返回。
晕了?从头再来看看提交的过程:
1:submit(FutureTask(Runnable)):AbstractExecutorService
public Future<?> submit(Runnable task) { if (task == null) throw new NullPointerException(); RunnableFuture<Void> ftask = newTaskFor(task, null); execute(ftask); return ftask; }
protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) { return new FutureTask<T>(runnable, value); }
2:生成三个FutureTask(其中runnable就是同一个底层FutureTask任务):
public FutureTask(Runnable runnable, V result) { this.callable = Executors.callable(runnable, result); this.state = NEW; // ensure visibility of callable }
3:调用Executors.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() { //直接调用底层同一个FutureTask的run(); task.run(); return result; } }
即三次提交,生成三份FutureTask,每份FutureTask调用Executors.callable()为自己底层的callable赋值,而Executors.callable方法生成简单的Callable实现,其中call(),调用底层共同FutureTask的run(), 也就受共同futureTask内部状态(state, runThread)限制。所以,阻塞在底层共同FutureTask阻塞队列中的只有一个线程,看下例:
public class Test { public static void main(String[] args) { ExecutorService executor = Executors.newCachedThreadPool(); Task task = new Task(); int waitTime = 4000; CountDownLatch latch = new CountDownLatch(1); FutureTask<Integer> futureTask = new FutureTask<Integer>(task); for (int i = 0 ; i < 3; i++) { new Thread(new Submit(executor, futureTask, latch, waitTime)).start(); } try { Thread.sleep(3000); latch.countDown(); Thread.sleep(8000); } catch (InterruptedException e1) { e1.printStackTrace(); } System.out.println("所有任务执行完毕"); executor.shutdown(); } } class Submit implements Runnable { private CountDownLatch latch ; private ExecutorService es ; private FutureTask<Integer> task; private int waitTime ; public Submit(ExecutorService es, FutureTask<Integer> task, CountDownLatch latch, int waitTime) { this.latch = latch; this.es = es; this.task = task; this.waitTime = waitTime; } public void run() { try { latch.await(); Future<?> future = es.submit(task); System.out.println("Thread name : " + Thread.currentThread().getName() + " go!"); waitTime = new Random().nextInt(waitTime); System.out.println("Thread name : " + Thread.currentThread().getName() + " , The wait time : = " + waitTime ); future.get(waitTime, TimeUnit.MILLISECONDS); System.out.println("Thread name : " + Thread.currentThread().getName() + " run over!"); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e1) { e1.printStackTrace(); } catch (TimeoutException e2) { System.err.println("Thread name : " + Thread.currentThread().getName() + " " + e2); } } } class Task implements Callable<Integer>{ public Integer call() throws Exception { System.out.println("thread name : " + Thread.currentThread().getName() + " do the work!"); Thread.sleep(4000); int sum = 0; for(int i=0;i<20;i++) sum += i; return sum; } } class Task1 implements Runnable{ int sum = 0; @Override public void run() { System.out.println("Thread Name : " + Thread.currentThread().getName() + "do the work!"); try { Thread.sleep(6000); for(int i=0;i<100;i++) sum += i; } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
显示结果:
Thread name : Thread-2 go! Thread name : Thread-0 go! Thread name : Thread-0 , The wait time : = 2738 thread name : pool-1-thread-1 do the work! Thread name : Thread-1 go! Thread name : Thread-2 , The wait time : = 284 Thread name : Thread-1 , The wait time : = 678 Thread name : Thread-2 run over! Thread name : Thread-0 run over! Thread name : Thread-1 java.util.concurrent.TimeoutException 所有任务执行完毕
三个线程都是阻塞一段时间,但是只有一个超时,另外两个运行完毕,(他两实际工作那部分没运行,处理各自FutureTask那部分代码,所以只能看到线程池只有一个线程处理底层FutureTask);
但,如果直接并发提交Callable,或者Runnable,线程池会启动三个线程来分别处理这三个不同任务,朋友可以自行试验demo下。而FutureTask是自己的自身的限制。
后话,一般调用ThreadPoolExecutor.submit()提交的是Callable<T>和Runnable, 返回的Future<T>, Future<?>(返回Null,或者不要求返回值),提交FutureTask用不着,所以实际中不会遇见这种情况。
另外,本文源码基于jdk1.7,与网上1.7之前源码不同(1.6通过AQS实现)。