线程池核心线程数设为 0 会怎样?(附源码解析)

0 阅读3分钟

不知道大家学习线程池时有没有这个疑问,创建线程池时如果用的无界队列(或者比较大的队列),核心线程数设置为 0,按照网上说的线程池的任务执行流程,队列不满不会创建非核心线程,那不炸了吗?任务永远执行不了?

网上普遍的线程池工作流程图:

先说结论

即使核心线程数设置为 0,提交一个任务后线程池也会创建一个线程去执行

代码验证

  1. 核心线程数为 0,线程池中会创建一个线程,任务会由这个线程顺序执行
public static void main(String[] args) throws InterruptedException {
        // 配置:核心 0,最大 5,存活 60 秒
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
                0, 5, 60, TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(100)
        );

        // 提交 10 个任务
        for (int i = 0; i < 10; i++) {
            int cur = i;

            executor.execute(() -> {
                System.out.println(cur +"执行,线程:" + Thread.currentThread().getName());
            });
        }

}
  1. 为了更加清楚执行过程中线程池的情况:
    public static void main(String[] args) throws InterruptedException {
        // 配置:核心 0,最大 5,存活 60 秒
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
                0, 5, 60, TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(100)
        );
        // 提交 3 个任务
        for (int i = 0; i < 3; i++) {
            int cur = i;

            executor.execute(() -> {
                System.out.println(cur + "执行,线程:" + Thread.currentThread().getName());
                try {
                    Thread.sleep(500); // 模拟任务执行时间
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            });

            // 打印当前线程池状态
            System.out.println("=== 提交任务 " + i + " 后 ===");
            System.out.println("核心线程数:" + executor.getCorePoolSize());
            System.out.println("最大线程数:" + executor.getMaximumPoolSize());
            System.out.println("当前线程数:" + executor.getPoolSize());
            System.out.println("活跃线程数:" + executor.getActiveCount());
            System.out.println("队列大小:" + executor.getQueue().size());
            System.out.println("完成任务数:" + executor.getCompletedTaskCount());
            System.out.println("========================\n");

            Thread.sleep(1000);
        }

        // 等待所有任务完成
        Thread.sleep(3000);

        System.out.println("\n=== 最终状态 ===");
        System.out.println("当前线程数:" + executor.getPoolSize());
        System.out.println("活跃线程数:" + executor.getActiveCount());
        System.out.println("队列大小:" + executor.getQueue().size());
        System.out.println("完成任务数:" + executor.getCompletedTaskCount());
    }

可以看出线程池中始终只有一个线程在处理任务

  1. 再看一下正常情况下,比如设置核心线程数为 2,会有两个线程会并发执行。
public static void main(String[] args) throws InterruptedException {
    // 配置:核心 2,最大 5,存活 60 秒
    ThreadPoolExecutor executor = new ThreadPoolExecutor(
        2, 5, 60, TimeUnit.SECONDS,
        new LinkedBlockingQueue<>(100)
    );

    // 提交 10 个任务
    for (int i = 0; i < 10; i++) {
        int cur = i;

        executor.execute(() -> {
            System.out.println(cur +"执行,线程:" + Thread.currentThread().getName());
        });
    }

}

源码分析(debug)

一层层扒开源码看一下整个流程。

  1. 任务提交时,可以看到线程池无线程

  1. 走到 execute 方法里面,这时可以队列中加入一个任务,接着会走进workerCountOf(recheck) == 0这个判断里(即线程数为 0),这是个兜底策略,针对核心线程数为 0 的特殊情况

execute完整源码:

public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();

        int c = ctl.get();
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            if (! isRunning(recheck) && remove(command))
                reject(command);
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        else if (!addWorker(command, false))
            reject(command);
}
  1. 接着进入addWorker 方法,就可以看到这里会创建一个线程去执行任务,可以看到线程数为 1

addWorker 完整源码:

private boolean addWorker(Runnable firstTask, boolean core) {
        retry:
        for (int c = ctl.get();;) {
            // Check if queue empty only if necessary.
            if (runStateAtLeast(c, SHUTDOWN)
                && (runStateAtLeast(c, STOP)
                    || firstTask != null
                    || workQueue.isEmpty()))
                return false;

            for (;;) {
                if (workerCountOf(c)
                    >= ((core ? corePoolSize : maximumPoolSize) & COUNT_MASK))
                    return false;
                if (compareAndIncrementWorkerCount(c))
                    break retry;
                c = ctl.get();  // Re-read ctl
                if (runStateAtLeast(c, SHUTDOWN))
                    continue retry;
                // else CAS failed due to workerCount change; retry inner loop
            }
        }

        boolean workerStarted = false;
        boolean workerAdded = false;
        Worker w = null;
        try {
            w = new Worker(firstTask);
            final Thread t = w.thread;
            if (t != null) {
                final ReentrantLock mainLock = this.mainLock;
                mainLock.lock();
                try {
                    // Recheck while holding lock.
                    // Back out on ThreadFactory failure or if
                    // shut down before lock acquired.
                    int c = ctl.get();

                    if (isRunning(c) ||
                        (runStateLessThan(c, STOP) && firstTask == null)) {
                        if (t.getState() != Thread.State.NEW)
                            throw new IllegalThreadStateException();
                        workers.add(w);
                        workerAdded = true;
                        int s = workers.size();
                        if (s > largestPoolSize)
                            largestPoolSize = s;
                    }
                } finally {
                    mainLock.unlock();
                }
                if (workerAdded) {
                    t.start();
                    workerStarted = true;
                }
            }
        } finally {
            if (! workerStarted)
                addWorkerFailed(w);
        }
        return workerStarted;
    }

最后

你以为是 bug? 设计线程池的大佬早就预料到了!