一般我们都知道线程池初始化的时候会设置核心线程数CorePoolSize 这个数量代表着我们把要执行的线程丢入到线程池中的工作线程执行 如果当前工作线程数小于等于核心线程数 执行完以后不会把这个工作线程销毁 而是一直等待 除非线程池关闭 不然这个线程会一直存在 这个也是线程池的核心作用 减少线程一直创建销毁带来的损耗
//工作线程 详细我在上面的链接里有介绍
HashSet workers
上面是正常情况下线程池的运行情况 不过有两种可能会打破这种情况 一个就是allowCoreThreadTimeOut参数 这个如果为true 则会给核心线程数设置超时等待时间 如果超过时间了 就会销毁 默认是false 这个参数不是这次的重点 因为默认是关闭的 一般不会特意开启 另一个情况也是这次重点说的 就是当我们实现Runnable的run方法 会在run里写具体的业务逻辑 可如果在run方法中如果出现异常 又没有捕获 导致抛出会怎么样呢 可以看下面的代码ThreadPoolExecutor#runWorker 这里是线程池核心执行线程的方法 为了方便看核心代码 所以删除了一些和这次讲的无关的代码
final void runWorker(Worker w) {
Runnable task = w.firstTask;
boolean completedAbruptly = true;
try {
while (task != null || (task = getTask()) != null) {
try {
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++;
}
}
completedAbruptly = false;
} finally {
processWorkerExit(w, completedAbruptly);
}
}
上面可以看到有个while循环 如果当前线程是核心线程数 getTask()获取需要执行的任务 如果没有会阻塞等待 有则执行 也就是说正常情况下核心线程会一直在这个while循环中 不会结束 也是这里才会让线程池的核心线程不会销毁 而如果执行task.run(); (开发人员自己实现的run方法代码)异常的时候 则会退出循环 然后执行processWorkerExit这个方法 然后再看processWorkerExit方法 删除了部分代码
private void processWorkerExit(Worker w, boolean completedAbruptly) {
try {
completedTaskCount += w.completedTasks;
//删除当前线程
workers.remove(w);
} finally {
mainLock.unlock();
}
if (runStateLessThan(c, STOP)) {
//创建一个空的工作线程
addWorker(null, false);
}
}
可以看到上面的代码 其实就是销毁一个核心线程数 然后再创建一个核心线程数 也就是说如果run里常常会抛异常 哪怕设置了核心线程数 其实这个线程也是常常销毁然后再创建一个新的 所以run方法里开发人员捕捉全部的异常 而不是向上抛出