各位大佬在面试过程肯定会被问到线程池或者多线程的问题,例如:
这些问题是相信稍微研究过线程池JDK源码的都能掌握。有兴趣的可以参数这篇博文。
在进入今天正题之前,还是来看看第二个问题:线程池添加任务后,线程池内部是如何进行执行任务的。假设线程池中
线程池执行任务顺序如下:
在了解了线程池执行任务的原理后,再来看看如下几个问题
假设线程(简称T)执行任务A结束后,间隔5s后再向线程池添加任务B,思考这5s线程T是如何保活的或者说它是什么状态。添加任务B的线程是如何让线程T执行任务B的。如果不理解线程的状态转换和阻塞队列的原理,建议劝退。
小结:线程池利用阻塞队列实现任务的排队等待,而阻塞队列包含ReentrantLock属性,利用AQS的条件等待队列调用LockSupport.park(this)将自己设置为WAITING状态。
线程池提供了submit()方法来提交Callable任务,实际底层是将Callable包装成FutureTask(它实现了Runnable),然后调用execute()来提交任务

小结: submit()执行回调任务也是利用LockSupport来进行线程之间的值传递。
直接上代码:
public class ThreadPoolTest {
private static ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 2, 5, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(10));
public static void main(String[] args) throws ExecutionException, InterruptedException {
executor.execute(new RunnableTest(executor));
TimeUnit.SECONDS.sleep(5);
executor.setCorePoolSize(2);
executor.setMaximumPoolSize(3);
}
static class RunnableTest implements Runnable {
private ThreadPoolExecutor executor;
RunnableTest(ThreadPoolExecutor executor) {
this.executor = executor;
}
@Override
public void run() {
while (true) {
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程数:" + executor.getPoolSize());
System.out.println("核心线程数:" + executor.getCorePoolSize());
System.out.println("最大线程数:" + executor.getMaximumPoolSize());
System.out.println("活跃数量:" + executor.getActiveCount());
}
}
}
}
线程池提供了两个set方法API设置核心线程数和最大线程数
线程池所谓核心线程数即线程池需要处理的任务个数小于等于核心线程数时,多余的线程会结束自己的生命周期。
回到线程保活的思路,线程在getTask()中阻塞在队列的take()方法中,而在这之前会判断当前线程池中线程数量是否大于核心线程数,小于则会阻塞在take()中,大于则调用poll(aliveTime)进行阻塞,底层会调用LockSupport.parkNanos(this, aliveTime)。在aliveTime时间范围内,如果没有多余的任务需要执行,则该线程不会再被唤醒。过了aliveTime时间后,该线程自动唤醒跳出循环执行processWorkerExit()。最终调用Thread.interrupt()方法退出。