关于线程池的几个问题?

参考:

  • 它的核心参数是哪些?
  • 添加任务的逻辑是什么?
  • 获取并执行任务的逻辑是什么?
  • 当线程池空闲时怎么删除多余线程的?
  • 有哪些需要特别注意的点?

它的核心参数是哪些

  • corePoolSize : 任务队列未达到队列容量时,最大可以同时运行的线程数量。
  • maximumPoolSize : 任务队列中存放的任务达到队列容量的时候,当前可以同时运行的线程数量变为最大线程数。
  • workQueue: 新任务来的时候会先判断当前运行的线程数量是否达到核心线程数,如果达到的话,新任务就会被存放在队列中。
  • handler :拒绝策略

添加任务的逻辑是什么

  1. 如果当前运行的线程数小于核心线程数,那么就会新建一个线程来执行任务。
  • (假如corePoolSize=5,此时只有1个任务在运行中,又添加1个任务,还是会创建新线程。)
  • (假如corePoolSize=5,此时已成功运行过3个任务,又添加1个任务,还是会创建新线程,而不是复用这3个线程。)
  1. 如果当前运行的线程数等于或大于核心线程数,但是小于最大线程数,那么就把该任务放入到任务队列里等待执行。
  2. 如果向任务队列投放任务失败(任务队列已经满了),但是当前运行的线程数是小于最大线程数的,就新建一个线程来执行任务。
  3. 如果当前运行的线程数已经等同于最大线程数了,新建线程将会使当前运行的线程超出最大线程数,那么当前任务会被拒绝,拒绝策略会调用RejectedExecutionHandler.rejectedExecution()方法。

获取并执行任务的逻辑是什么

因为我们提交的任务Runnabale是以Worker这个对象去运行。

// 在java.util.concurrent.ThreadPoolExecutor中
private final class Worker
    extends AbstractQueuedSynchronizer
    implements Runnable
{
}

java.util.concurrent.ThreadPoolExecutor#addWorker中会创建Worker,并调用Worker#thread的start()方法。

// 在java.util.concurrent.ThreadPoolExecutor中
Worker w = null;
try {
    // 此处创建Worker
    w = new Worker(firstTask);
    final Thread t = w.thread;
    if (t != null) {
        ...
        if (workerAdded) {
            // 此处执行线程t
            container.start(t);
            ...
        }
    }
}

// new Worker(firstTask)
Worker(Runnable firstTask) {
    setState(-1); // inhibit interrupts until runWorker
    this.firstTask = firstTask;
    // 注意:创建的thread中是把当前worker传进去的
    this.thread = getThreadFactory().newThread(this);
}

container.start(t)跟着调用到这里。

// 在java.lang.System中
public void start(Thread thread, ThreadContainer container) {
    thread.start(container);
}

最终会调用到Worker的run()方法,别忘记它是一个Runnable。

// 在java.util.concurrent.ThreadPoolExecutor.Worker中
public void run() {
    runWorker(this);
}

runWorker会使用firstTask或从workQueue中获取任务去执行。

// 在java.util.concurrent.ThreadPoolExecutor中
private Runnable getTask() {
    ...
    for (;;) {
        int c = ctl.get();
        int rs = runStateOf(c);
        // 当我们调用shutdown()的时候 线程池状态是SHUTDOWN,调用shutdownNow()的时候线程状态是STOP,那么这2种状态是怎么处理阻塞队列里面的任务的呢?代码如下,
        // 当状态是SHUTDOWN的时候,只有 队列为空的时候 才会返回null
        // 当状态是STOP的时候,直接返回的null
        // 说明 SHUTDOWN<STOP
        if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
            decrementWorkerCount();
            return null;
        }
        ...
}

最后 用一张流程图,来描述下一个任务从添加到运行结束,经历了哪些方法!

invalid image(图片无法加载)

说明:图片来自ThreadPoolExecutor是怎么去执行一个任务的?前面一遍文章 我们看了下FutureTask的源码,知道了怎 - 掘金

作者:张三  创建时间:2025-02-28 19:20
最后编辑:张三  更新时间:2025-03-05 18:56