前提:最近看了别人写的一个关于线程池的,发现好像不太了解,本着代码不会骗人的思想,亲手实验一把。
有的线程它活着,但它躺在池中碌碌无为;有的线程它死了,于是它变成一道面试题。看到这句话不知道为啥有点心酸又有点想笑。
一个线程池中的线程异常了,那么线程池会怎么处理这个线程?
看到这个的时候我的一个反应是:抛出异常,根据线程池定义的策略进行后续的处理,想想看自己“会的真多”。那么代码敲起来,没代码,假把式
public class ExecutorsTestV1 {
public static void main(String[] args) {
ThreadPoolExecutor poolExecutor = buildThreadPoolExecutor();
poolExecutor.execute(()->getThingsDone("execute"));
poolExecutor.submit(()->getThingsDone("submit"));
}
private static ThreadPoolExecutor buildThreadPoolExecutor(){
int coreSize = 2;
int maxSize = 10;
long keepAliveTime = 30*1000;
LinkedBlockingQueue linkedBlockingQueue = new LinkedBlockingQueue(10);
ThreadFactory defaultThreadFactory = Executors.defaultThreadFactory();
RejectedExecutionHandler defaultHandler = new ThreadPoolExecutor.AbortPolicy();
/**
* TODO 手动构造一个线程池的参数
* int corePoolSize,
* int maximumPoolSize,
* long keepAliveTime,
* TimeUnit unit,
* BlockingQueue<Runnable> workQueue,
* ThreadFactory threadFactory,
* RejectedExecutionHandler handler
*/
ThreadPoolExecutor threadPoolExecutor =
new ThreadPoolExecutor(coreSize,maxSize,keepAliveTime,TimeUnit.SECONDS
,linkedBlockingQueue,defaultThreadFactory,defaultHandler);
return threadPoolExecutor;
}
private static void getThingsDone(String name){
String errorMsg = "【 thread-name:"+Thread.currentThread().getName()+" ,搞事情 :"+name+"】";
System.out.println(errorMsg);
throw new RuntimeException(errorMsg+" , 搞事情抛异常!....");
}
}
executor运行时的异常再发生后 再java.util.concurrent.ThreadPoolExecutor#runWorker中被catch了 并等到运行后执行afterExecute。
那么afterExecute执行后这么异常实在什么地方被处理的呢? 这里要说一下关于Thread 和 ThreadGroup ,在Thread init() 方法中可以看到,Thread 会判断是否有 ThreadGroup (参数) ,没有的话从系统中或parent中获取,看起来好像没什么用,敲代码的时候好像没用过? 不急慢慢来。
private void init(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc)
....
Thread parent = currentThread();
SecurityManager security = System.getSecurityManager();
if (g == null) {
/* Determine if it's an applet or not */
/* If there is a security manager, ask the security manager
what to do. */
if (security != null) {
g = security.getThreadGroup();
}
/* If the security doesn't have a strong opinion of the matter
use the parent thread group. */
if (g == null) {
g = parent.getThreadGroup();
}
}
....
this.group = g;
....
再看看ThreadGroup对象,翻翻源码看看它在搞什么,里面有一个不看不知道的方法:uncaughtException(Thread t, Throwable e) 源码的文档这么写的:
/**
* Called by the Java Virtual Machine when a thread in this
* thread group stops because of an uncaught exception, and the thread
* does not have a specific {@link Thread.UncaughtExceptionHandler}
* installed.
手动翻译下:当一个thread 在这个 threadgroup 下因为一个 an uncaught exception 而停止,并且该线程没有对应的Thread.UncaughtExceptionHandler(异常处理策略)的时候,这个方法会被JVM 调用
看完后,反过来看看我们自定义的线程池
好像没有关于UncaughtExceptionHandler的定义,来先打个断点,让程序跑一会,毕竟源码不会骗人的,果然进来了
看完了execute() 方法的执行过程,那么submit没有直接抛出异常?源码下一切揭晓:
/**
* @throws RejectedExecutionException {@inheritDoc}
* @throws NullPointerException {@inheritDoc}
*/
public Future<?> submit(Runnable task) {
if (task == null) throw new NullPointerException();
RunnableFuture<Void> ftask = newTaskFor(task, null); // 变成FutureTask
execute(ftask); // 调用的还是execute 方法 不过把返回的结果放在了Future 对象中
return ftask;
}
然后当前的 state 是 0 (代表的是 NEW 状态),因为发生异常所以会被改为3(EXCEPTIONAL),然后Future.get() 方法获取FutureTask的执行结果。
看完了代码一切觉得完美,多给写结果线程试试效果,事事不如意十之八九,看看感觉和自己的推理不太一样啊,五个线程为什么出现1,2,3,5,6,如果没有断点的话显示的是1,2,3,4,5,多试几次还出现不同的情况,这是什么意思?
之前的源码中找到添加task 的地方,然后看看是如何添加的以及线程id 的初始化方式,以及runWorker方法+processWorkerExit方法
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 pool is stopping, ensure thread is interrupted;
// if not, ensure thread is not interrupted. This
// requires a recheck in second case to deal with
// shutdownNow race while clearing interrupt
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);
}
}
private void processWorkerExit(Worker w, boolean completedAbruptly) {
if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
decrementWorkerCount();
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
completedTaskCount += w.completedTasks;
// 移除当前发生异常的Worker !!! 所以发生异常的线程会被移除!!!
workers.remove(w);
} finally {
mainLock.unlock();
}
tryTerminate();
int c = ctl.get();
if (runStateLessThan(c, STOP)) {
if (!completedAbruptly) {
int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
if (min == 0 && ! workQueue.isEmpty())
min = 1;
if (workerCountOf(c) >= min)
return; // replacement not needed
}
// 移除之后会创建一个新的线程 !!!
addWorker(null, false);
}
}
第一眼看到者两个方法的时候其实有点奇怪,为什么看起来这个processWorkerExit方法不管是正常还是异常都会执行?,不应该啊,异常的时候可以理解正常的任务没必要执行的,结合前一个问题,发现正常的时候并没有执行processWorkerExit方法,那么为什么?从什么地方跳出去了?,debug跑起来多来几个断点,惊喜到处有。
runWorker中有一行代码 while (task != null || (task = getTask()) != null) 看起来很平常,进去看看不得了。
private Runnable getTask() {
boolean timedOut = false; // Did the last poll() time out?
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// Check if queue empty only if necessary.
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount();
return null;
}
int wc = workerCountOf(c);
// Are workers subject to culling?
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
if (compareAndDecrementWorkerCount(c))
return null;
continue;
}
try {
// 这里这里!!!!
//
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take(); // workQueue 是一个阻塞队列,当没有任务时会阻塞
if (r != null)
return r;
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
/**********java.util.concurrent.LinkedBlockingQueue#take**************/
public E take() throws InterruptedException {
E x;
int c = -1;
final AtomicInteger count = this.count;
final ReentrantLock takeLock = this.takeLock;
takeLock.lockInterruptibly();
try {
while (count.get() == 0) {
// notEmpty ==》 Condition notEmpty
notEmpty.await();
}
........
}
/***java.util.concurrent.locks.AbstractQueuedSynchronizer.ConditionObject#await()********/
public final void await() throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
Node node = addConditionWaiter();
int savedState = fullyRelease(node);
int interruptMode = 0;
while (!isOnSyncQueue(node)) {
// 这里这里
// LockSupport.park :除非有许可,否则出于线程调度目的禁用当前线程。
LockSupport.park(this);
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
......
}
所以正常的worker并没有执行processWorkerExit方法,而报异常的会执行processWorkerExit。
看完了代码之后感觉脸被打了,有点疼。平时还是要多敲敲代码,看看源码。
文章搬的这个大佬的:https://mp.weixin.qq.com/s?__biz=MzIxNTQ4MzE1NA==&mid=2247483741&idx=1&sn=238fc933c3b9b19ab1754b23283ac6fd&chksm=9796d720a0e15e364f4105c29de606e7329760a41607136a722dc97bb177f9362aeacd92f762&scene=126&sessionid=1591885069&key=2a95b34f7843ebbf206c92fcb812bfa083e39d317d50216afc66c5ac6d6a1d860b6c8b0285ce8e1234b9bd8242b0582ab430f9f58e7f33fb856ae0023d8ee87e89ac66405d0febcfa66827b13f522079&ascene=1&uin=MTg5MjI0NzkwMg%3D%3D&devicetype=Windows+10+x64&version=62090070&lang=zh_CN&exportkey=AQjS9ua1GI5JCsbSbUs6fj4%3D&pass_ticket=Y5XwQHmAveCFA1CZXgq%2Bo2xHOW19vm5pu8rjrS4nkvNG2P05cWPzC%2BWbuMsqTdQJ