前言
使用線程池難免會(huì)用到submit和execute,但是submit是有坑的侵歇,此處做個(gè)記錄
1潦蝇、submit坑
此處隨便寫(xiě)一個(gè)方法,進(jìn)入內(nèi)部查看execute和submit
/**
* @Author: 小混蛋
* @CreateDate: 2018/8/29 9:58
*/
@Component
public class Test {
public static void main(String[] args) {
ExecutorService es = Executors.newFixedThreadPool(5);
ArrayList<Future<?>> arrayList = new ArrayList();
for (int i = 0; i < 10; i++) {
final int b = i;
Future<?> submit = es.submit(() -> {
System.out.println(Thread.currentThread().getName());
int a = b / 0;
});
arrayList.add(submit);
}
arrayList.forEach(s -> {
try {
s.get();
} catch (InterruptedException |ExecutionException e) {
e.printStackTrace();
}
});
es.shutdown();
}
@Scheduled(cron = "")
public void test() {
}
}
ctrl加鼠標(biāo)左鍵進(jìn)入submit个唧,查看AbstractExecutorService,發(fā)現(xiàn)submit底層調(diào)用的還是execute设预,但是提交的任務(wù)不是task徙歼,而是在task的基礎(chǔ)上封裝了一層FutureTask
public Future<?> submit(Runnable task) {
if (task == null) throw new NullPointerException();
RunnableFuture<Void> ftask = newTaskFor(task, null);
execute(ftask);
return ftask;
}
重點(diǎn)來(lái)了,當(dāng)submit提交的task里面出現(xiàn)未檢查異常如RuntimeException和Error等鳖枕,直接execute你的task肯定是拋異常魄梯;但是使用submit之后提交的FutureTask我們看下它的源碼run方法:run方法和我們直接提交的task的run方法并不一樣,該方法會(huì)對(duì)所有的Throwable類型進(jìn)行捕獲宾符,并把異常通過(guò)setException保存在內(nèi)部變量outcome里面酿秸。所以線程池執(zhí)行的過(guò)程中異常不會(huì)被拋出
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);
}
}
protected void setException(Throwable t) {
if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
outcome = t;
UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final state
finishCompletion();
}
}
另一個(gè)重點(diǎn)來(lái)了,當(dāng)submit被futuretask.get的時(shí)候魏烫。會(huì)在report方法調(diào)用過(guò)程中拋出這個(gè)未檢查異常辣苏!
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);
}
結(jié)論
1、submit在執(zhí)行過(guò)程中與execute不一樣则奥,不會(huì)拋出異常而是把異常保存在成員變量中考润,在FutureTask.get阻塞獲取的時(shí)候再把異常拋出來(lái)狭园。
2读处、Spring的@Schedule注解的內(nèi)部實(shí)現(xiàn)就是使用submit,因此唱矛,如果你構(gòu)建的任務(wù)內(nèi)部有未檢查異常罚舱,你是永遠(yuǎn)也拿不到這個(gè)異常的井辜。
3、execute直接拋出異常之后線程就死掉了管闷,submit保存異常線程沒(méi)有死掉粥脚,因此execute的線程池可能會(huì)出現(xiàn)沒(méi)有意義的情況,因?yàn)榫€程沒(méi)有得到重用包个。而submit不會(huì)出現(xiàn)這種情況刷允。