java-源码解读-线程池提交之execute和submit有何不同
2017-09-28 14:42
791 查看
先上测试用例
package com.phl.threadpool;import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
/**
* @Title:SubmitExecuteMain
* @Description:
* @Copyright:中国电信爱wifi运营中心
* @author:panhl
* @date 2017/9/26 0026 10:34
*/
public class SubmitExecuteMain {
public static void main(String[] args) throws Exception{
submit();
submitWithGet();
execute();
}
private static void submitWithGet() throws Exception{
ExecutorService service= Executors.newSingleThreadExecutor();
Future future=service.submit(new Runnable() {
@Override
public void run() {
int i=7/0;
}
});
service.shutdown(); future.get(); }private static void submit(){ ExecutorService service= Executors.newSingleThreadExecutor(); service.submit(new Runnable() { @Override public void run() { int i=7/0; } }); service.shutdown(); } private static void execute(){ ExecutorService service= Executors.newSingleThreadExecutor(); service.execute(new Runnable() { @Override public void run() { int i=7/0; } }); service.shutdown(); }}
先下结论
1.对返回值的处理不同execute方法不关心返回值。
submit方法有返回值,Future。
2.对异常的处理不同
excute方法会抛出异常。
sumbit方法不会抛出异常。除非你调用Future.get()。
再看原理
当我们调用ExecutorService的submit的时候,其实是调用了AbstractExecutorService.submit方法。直接看源码:
public Future<?> submit(Runnable task) { if (task == null) throw new NullPointerException(); RunnableFuture<Void> ftask = newTaskFor(task, null); execute(ftask); return ftask; }从上面的代码可以看出 execute和sumbit方法的不同在于这一行封装:
RunnableFuture<Void> ftask = newTaskFor(task, null);这就是关键所在。
先看execute方法
当我们调用execute时,以ThreadPoolExecutor实现为例,他就是调用了ThreadPoolExecutor的execute方法。public void execute(Runnable command) { if (command == null) throw new NullPointerException(); int c = ctl.get(); if (workerCountOf(c) < corePoolSize) { if (addWorker(command, true)) return; c = ctl.get(); } if (isRunning(c) && workQueue.offer(command)) { int recheck = ctl.get(); if (! isRunning(recheck) && remove(command)) reject(command); else if (workerCountOf(recheck) == 0) addWorker(null, false); } else if (!addWorker(command, false)) reject(command); }这里不分析 execute方法内部实现,我想说的是ExecutorService的execute方法提交的任务被原模原样的的转交给了实现类ThreadPoolExecutor的execute方法。
也就是说原来提交的是Runnable,执行的也是Runnable.
具体提交任务流程不在这里作分析,最后直接看任务的执行代码:ThreadPoolExecutor.runWorker final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock(); // allow interrupts
boolean completedAbruptly = true;
try {
while (task != null || (task = getTask()) != null) {
w.lock();
if ((runStateAtLeast(ctl.get(), STOP) ||
(Thread.interrupted() &&
runStateAtLeast(ctl.get(), STOP))) &&
!wt.isInterrupted())
wt.interrupt();
try {
beforeExecute(wt, task);
Throwable thrown = null;
try {
task.run();
} catch (RuntimeException x) {
thrown = x; throw x;
} catch (Error x) {
thrown = x; throw x;
} catch (Throwable x) {
thrown = x; throw new Error(x);
} finally {
afterExecute(task, thrown);
}
} finally {
task = null;
w.completedTasks++;
w.unlock();
}
}
completedAbruptly = false;
} finally {
processWorkerExit(w, completedAbruptly);
}
}真正执行的地方在task.run这一行,task 就是原模原样提交的Runnable,也就是执行了Runnable.run方法。有异常就抛异常。
再看sumbit方法
sumbit方法的不同之处在于上面提到的那一行,对Runnable的封装。RunnableFuture<Void> ftask = newTaskFor(task, null);直接看newTaskFor方法把 Runnable封装成了什么东西:protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) { return new FutureTask<T>(runnable, value); } public FutureTask(Runnable runnable, V result) { this.callable = Executors.callable(runnable, result); this.state = NEW; // ensure visibility of callable }Runnable转变过程:
Runnable-----newTaskFor----->new FutureTask()------->FutureTask
任务的转变过程:
Runnable.run()-----Executors.callable(runnable, result)------->Callable.call()
通过上面两个方法,Runnable已经转变成了FutureTask。
关于FutureTask ,请参考我的另一篇博客:java-源码解读-FutureTask
通过上面对execute方法的分析,我们知道runWorker 方法中那句task.run()其实也就是FutureTask.run()了。
直接看FutureTask.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 = null; int s = state; if (s >= INTERRUPTING) handlePossibleCancellationInterrupt(s); } }代码很清楚,run方法中真正执行任务的地方为c.call(),也就是调用Callable.call(),当c.call()发生异常时 catch了,并调用了setExcetion方法:
protected void setException(Throwable t) { if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) { outcome = t; UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final state finishCompletion(); } }setException把抛出的异常赋值给了outcome,outcome就是Futer.get() 的返回结果。
所以就有了开始的结论,submit提交任务时不会有异常,因为异常被当作结果返回了。
要想submit方法也抛出异常,可以调用 Future.get(); Future的get 方法如下: 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;
if (s == NORMAL)
return (V)x;
if (s >= CANCELLED)
throw new CancellationException();
throw new ExecutionException((Throwable)x);
}
相关文章推荐
- 一心多用多线程-细谈java线程池submit与execute的区别
- java-源码解读-线程池实现原理-1
- java-源码解读-线程池实现原理-0
- Java 线程池submit和execute
- 超详细的java线程池源码解读
- Spark学习之路 (十六)SparkCore的源码解读(二)spark-submit提交脚本
- Java JDBC下执行SQL的不同方式executeQuery/executeUpdate/execute
- Java多线程-线程池ThreadPoolExecutor的submit返回值Future
- Java线程池架构原理和源码解析(ThreadPoolExecutor)
- Java并发之线程池ThreadPoolExecutor源码分析学习
- 线程池的submit和execute方法区别
- Java之LinkedList源码解读(JDK 1.8)
- Java源码解读:顶级父类Object
- 一个表单form、submit提交到多个不同页面
- Java 集合深入理解(16):HashMap 主要特点和关键方法源码解读
- (四) Java多线程详解之四种不同线程池使用详解
- Java 集合深入理解(16):HashMap 主要特点和关键方法源码解读
- 关于Java中线程池的解读
- Java多线程并发之 fork jion ,不同线程之间互帮互助,最快完成线程池任务。
- Java源码解读之java.util.ArrayList