之前的一篇线程池 submit()执行怎么获取到的返回值只是讲了怎么使用,还有一部分源码。
这篇具体从 提交任务,到具体怎么 传入Runnable 会可以有返回值?并且还涉及线程池中线程怎么执行任务?
概念级的区别可以看之前的文章。
- 从submit构造方法开始
RunnableFuturesubmit()的出现在ExecutorService,具体实现是它的实现类AbstractExecutorService 有三个构造方法 <T> Future<T> submit(Callable<T> task); <T> Future<T> submit(Runnable task, T result); Future<?> submit(Runnable task); 第一个就直接使用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(Callable<T> task) { if (task == null) throw new NullPointerException(); RunnableFuture<T> ftask = newTaskFor(task); 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; } 主要调用就是这个方法 protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) { //直接new了一个futureTask return new FutureTask<T>(runnable, value); } 这三个方法体几乎一模一样。那么具体的区别在哪里?前提先了解一下RunnableFuture和FutureTask
FutureTaskpublic interface RunnableFuture<V> extends Runnable, Future<V> { void run(); } 实现了Runnable和Future。 子类为FutureTask public class FutureTask<V> implements RunnableFuture<V> 那么就明白了,Runnable转Callable并且有返回值,主要就应该在这个FutureTask中体现。
RunnableAdapter它一共有两个构造方法 public FutureTask(Runnable runnable, V result) { this.callable = Executors.callable(runnable, result); this.state = NEW; // ensure visibility of callable } public FutureTask(Callable<V> callable) { if (callable == null) throw new NullPointerException(); this.callable = callable; this.state = NEW; // ensure visibility of callable } 认真看一下就发现了 传入Callable直接赋值给类的成员变量 传入Runnable则是执行了一共方法,看一下这个方法,具体转化逻辑就有了 public static <T> Callable<T> callable(Runnable task, T result) { if (task == null) throw new NullPointerException(); return new RunnableAdapter<T>(task, result); } 一看又new了一个类。。。
这个是一个静态内部类。很短 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接口。并且在call()方法中,调用了task的run()。也就是相当于任务代码直接在call()方法中。 明白了Runnable是通过一个静态内部类 被转化为Callable了.并且执行完call()方法也会返回result。也可以看出,之前说我认为只是为了泛型的由来了。
- 接下来说一下线程池中线程是怎么执行任务的?
FutureTask.run()不说全部的代码,只说涉及到这部分的代码 在线程池的实现类ThreadPoolExecutor中线程执行任务的具体方法是runWorker() Worker是一个封装了线程的类。 这个方法中,会自旋获取任务。while (task != null || (task = getTask()) != null)。 如果是核心线程则不会从阻塞队列中获取任务。否则都会从阻塞队列中getTask() 在这个方法中,会task.run()这个run方法则是RunnableFuture中的run()方法. 具体实现在子类FutureTask中
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();//从这里可以看到,调用了call()方法 ran = true; } catch (Throwable ex) { result = null; ran = false; setException(ex); } if (ran) set(result);//这里将返回值设置到了outcome,然后通过get()获取 } } 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(); } } //复杂,是因为阻塞获取 public V get() throws InterruptedException, ExecutionException { int s = state; if (s <= COMPLETING) s = awaitDone(false, 0L); return report(s); } private V report(int s) throws ExecutionException { Object x = outcome;//result if (s == NORMAL) return (V)x; if (s >= CANCELLED) throw new CancellationException(); throw new ExecutionException((Throwable)x); } 具体的流程: 线程获取任务执行 调用FutureTask.run() 调用Callable.call() 调用Runnable.run() 区别在于submit传入的是Callable,那么不用封装。传入的是Runnable则需要封装为Callable,通过一个实现了Callable接口的中间类RunnableAdpter
总结:源码中体现的区别,就在于要不要封装为RunnableAdpter。其余都一样。
还有一个注意的点,submit()提交的任务抛出了异常,不会被打印,而会被捕获到回调结果中,也就是get()返回的是异常结果
execute()则会打印到控制台
try {
result = c.call();
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex);
}
抛出异常会被捕获
protected void setException(Throwable t) {
if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
outcome = t;
UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final state
finishCompletion();
}
}
和set()一样。
线程抛出了异常,这个线程会被从线程哈希表中移除,取消强引用,让GC回收,并且重新创建一个新的线程。