Java 线程池(ThreadPoolExecutor)源码解析

Java 线程池(ThreadPoolExecutor)源码解析

一、前言

在高并发编程中,合理使用线程池可以有效提高系统的并发能力,降低线程创建和销毁的开销,并提升资源利用率。ThreadPoolExecutor 是 Java 线程池的核心实现类,我们今天将深入剖析其源码,理解线程池的工作机制。

二、线程池核心参数

ThreadPoolExecutor 源码中,线程池的核心参数如下:

public class ThreadPoolExecutor extends AbstractExecutorService {
    // 线程池状态 + 线程数量 控制变量
    private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));

    // 线程池的状态位
    private static final int RUNNING    = -1 << COUNT_BITS;
    private static final int SHUTDOWN   =  0 << COUNT_BITS;
    private static final int STOP       =  1 << COUNT_BITS;
    private static final int TIDYING    =  2 << COUNT_BITS;
    private static final int TERMINATED =  3 << COUNT_BITS;

    // 线程池的基本参数
    private volatile int corePoolSize; // 核心线程数
    private volatile int maximumPoolSize; // 最大线程数
    private volatile long keepAliveTime; // 线程存活时间
    private final BlockingQueue<Runnable> workQueue; // 任务队列
    private final RejectedExecutionHandler handler; // 拒绝策略
}

关键点解析

  1. ctl 变量:使用 高 3 位 存储线程池状态,低 29 位 存储线程数量。

  2. corePoolSizemaximumPoolSize 影响线程池的扩展能力。

  3. workQueue 存储等待执行的任务。

  4. handler 处理任务被拒绝的情况。

    三、任务提交(execute 方法)

public void execute(Runnable command) {
    if (command == null) throw new NullPointerException();
    int c = ctl.get();
    
    // 1. 线程数小于 corePoolSize,创建核心线程执行任务
    if (workerCountOf(c) < corePoolSize) {
        if (addWorker(command, true)) return;
        c = ctl.get();
    }
    
    // 2. 线程数已达 corePoolSize,任务入队
    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);
    }
    
    // 3. 队列已满,尝试创建非核心线程
    else if (!addWorker(command, false)) reject(command);
}

任务提交流程

  1. 如果当前线程数小于 **corePoolSize**,创建新线程执行任务。
  2. 如果线程数已达 **corePoolSize**,任务进入 **workQueue** 等待执行。
  3. 如果队列已满,尝试创建非核心线程执行任务。
  4. 如果非核心线程也无法创建,执行拒绝策略(**RejectedExecutionHandler**)。

四、线程创建(addWorker 方法)

 private boolean addWorker(Runnable firstTask, boolean core) {
        retry:
        for (;;) {
            int c = ctl.get();
            int rs = runStateOf(c);//  取高位,表示线程池的运行状态

            // Check if queue empty only if necessary.
            /**
             * 除非是:
             * 线程池是 SHUTDOWN 状态;
             * 要执行的任务是 null(说明是从队列里拿的任务);
             * 并且队列里还有任务;
             * 否则拒绝添加线程。
             */
            if (rs >= SHUTDOWN &&
                    ! (rs == SHUTDOWN &&
                            firstTask == null &&
                            ! workQueue.isEmpty()))
                return false;

            for (;;) {
                int wc = workerCountOf(c);
                if (wc >= CAPACITY || //CAPACITY: 表示线程数最大值(2^29 - 1)
                        wc >= (core ? corePoolSize : maximumPoolSize))
                    return false;
                if (compareAndIncrementWorkerCount(c))
                    break retry;
                c = ctl.get();  // Re-read ctl
                if (runStateOf(c) != rs)
                    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); // 封装任务为 Worker 对象
            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 rs = runStateOf(ctl.get());

                    if (rs < SHUTDOWN ||
                            (rs == SHUTDOWN && firstTask == null)) {
                        if (t.isAlive()) // precheck that t is startable //检查线程是否活着(理论上不能活);
                            throw new IllegalThreadStateException();
                        workers.add(w);
                        int s = workers.size();
                        if (s > largestPoolSize)
                            largestPoolSize = s;
                        workerAdded = true;
                    }
                } finally {
                    mainLock.unlock();
                }
                if (workerAdded) {
                    t.start(); //  添加完毕,启动线程
                    workerStarted = true;
                }
            }
        } finally {
            if (! workerStarted)
                addWorkerFailed(w); // 如果添加失败,则调用 addWorkerFailed 方法 回滚
        }
        return workerStarted;
    }

线程创建流程

  1. 检查线程池状态是否允许创建新线程。
  2. 如果线程数量未超过 **corePoolSize** **maximumPoolSize**,创建 **Worker** 线程。
  3. 启动线程,执行 **runWorker** 方法。

五、任务执行(runWorker 方法)

final void runWorker(Worker w) {
    Runnable task = w.firstTask;
    try {
        while (task != null || (task = getTask()) != null) {
            task.run();
        }
    } finally {
        processWorkerExit(w, false);
    }
}

任务执行流程

  1. 执行 **firstTask** 任务。
  2. 不断从 **workQueue** 获取任务并执行,直到没有任务。
  3. 当任务执行完毕,进行线程清理和回收。

六、线程回收(getTask 方法)

private Runnable getTask() {
    for (;;) {
        int c = ctl.get();
        boolean timedOut = false;
        
        if (workerCountOf(c) > corePoolSize) {
            Runnable r = workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS);
            if (r != null) return r;
            timedOut = true;
        } else {
            Runnable r = workQueue.take();
            if (r != null) return r;
        }
        
        if (timedOut) {
            decrementWorkerCount();
            return null;
        }
    }
}

线程回收机制

  • 线程池中的线程数大于 **corePoolSize**,则等待 **keepAliveTime** 后回收线程。
  • 如果 **corePoolSize** 内的线程,则会一直阻塞等待任务。
  • 超时后销毁线程,减少资源占用。

线程销毁(processWorkerExit 方法)

当线程池中的工作线程执行完任务并退出时,线程会进入销毁阶段。此时会调用 processWorkerExit 方法进行线程清理和资源释放。销毁的过程包括以下几个步骤:

private void processWorkerExit(Worker w, boolean completedAbruptly) {
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        // 从线程池中移除线程
        workers.remove(w);
        int c = ctl.get();
        // 更新线程池状态
        if (runStateOf(c) == TIDYING) {
            if (workerCountOf(c) == 0) {
                // 所有线程都退出后,线程池进入 TERMINATED 状态
                ctl.compareAndSet(c, ctlOf(TERMINATED, 0));
            }
        } else if (completedAbruptly) {
            // 如果线程是异常退出的,尝试重新创建线程
            addWorker(null, false);
        }
    } finally {
        mainLock.unlock();
    }
}

销毁过程解析

  1. 移除线程:当工作线程执行完任务并退出时,首先会从 workers 集合中移除对应的线程。

  2. 状态检查:通过检查线程池的状态,确定是否所有线程都已经退出。如果所有线程都已退出,线程池将进入 TERMINATED 状态。

  3. 异常退出处理:如果线程异常退出,线程池会尝试重新创建线程以保证线程池的稳定性。

特别:

ThreadPoolExecutor 中有多个扩展点,允许用户根据具体需求对其行为进行定制。以下是一些主要的扩展点:

1. RejectedExecutionHandler(拒绝策略)

  • 作用:当线程池无法处理提交的任务时,RejectedExecutionHandler 负责处理这些任务。默认的拒绝策略是 AbortPolicy,即抛出 RejectedExecutionException 异常。
  • 扩展方式:可以实现自己的拒绝策略,控制任务被拒绝时的行为。例如,CallerRunsPolicy 会让提交任务的线程来执行任务,或者通过 DiscardOldestPolicy 丢弃队列中最旧的任务。
  • 常用策略
    • AbortPolicy:抛出异常,默认策略。
    • CallerRunsPolicy:由调用线程来执行任务。
    • DiscardPolicy:丢弃当前任务,不抛出异常。
    • DiscardOldestPolicy:丢弃任务队列中最旧的任务。

2. ThreadFactory(线程工厂)

  • 作用ThreadFactory 用于定制线程的创建方式。例如,可以在每个线程的名称中加入自定义前缀,或者为线程设置特定的优先级等。

  • 扩展方式:通过实现 ThreadFactory 接口,可以自定义线程的创建过程。可以在 ThreadFactory 中设置线程的名称、守护线程标志、优先级等。

  • 示例

  • ThreadFactory customThreadFactory = new ThreadFactory() {
        @Override
        public Thread newThread(Runnable r) {
            Thread thread = new Thread(r);
            thread.setName("Custom-Thread-" + thread.getId());
            thread.setPriority(Thread.MAX_PRIORITY);
            return thread;
        }
    };
    
    

    3. BlockingQueue(任务队列)

    • 作用BlockingQueue 用于存储任务。线程池中的线程会从队列中获取任务并执行。默认使用的是 LinkedBlockingQueue,它会阻塞提交线程,直到队列中有空位。
    • 扩展方式:你可以使用不同类型的 BlockingQueue,例如 ArrayBlockingQueueSynchronousQueue,甚至可以自定义队列的行为来优化任务的存储和调度。
    • 注意:如果队列的容量有限,可能会影响线程池的性能,特别是当线程池满时。

    4. beforeExecuteafterExecute 钩子方法

    • 作用:这两个方法属于 ThreadPoolExecutor 类的 ThreadPoolExecutor 内部的 Worker 线程。通过重写这些方法,可以在任务执行前后执行自定义操作。

    • 扩展方式

      • beforeExecute(Thread t, Runnable r):在任务开始执行之前调用。
      • afterExecute(Runnable r, Throwable t):在任务执行完成后调用,可以用来记录日志或者执行清理工作。
    • 示例

      ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>());
      executor.setBeforeExecuteHandler((thread, task) -> {
          System.out.println("Task is about to execute");
      });
      executor.setAfterExecuteHandler((task, throwable) -> {
          System.out.println("Task has finished executing");
      });
      
      

      5. ThreadPoolExecutor 状态变化监听

      • 作用ThreadPoolExecutor 内部通过控制变量 ctl 来管理线程池的状态,可以监控和响应线程池的状态变化。
      • 扩展方式:可以通过观察 ThreadPoolExecutor 的状态变化,来执行一些额外的逻辑,例如在 TIDYINGTERMINATED 状态时执行清理操作。

      6. getQueue 方法

      • 作用:通过 getQueue() 方法,可以访问当前线程池的任务队列。默认的 BlockingQueueLinkedBlockingQueue,你可以通过继承 ThreadPoolExecutor 并重写该方法来更改队列的实现。
      • 扩展方式:通过获取队列对象,可以根据队列中的任务数来进行调优,比如动态调整队列大小或对队列进行监控。

记录:

1.线程池启动时 不会立刻创建核心线程,而是 有任务来时才创建,并且核心线程是按需创建的,一开始线程池是空的。

2.在线程池中,如果某个线程执行任务时发生了异常并退出(非正常完成),那么线程池会自动减少线程数,并尝试维持线程池的稳定状态(比如再补一个线程回来)

3.当线程池中的工作线程调用 getTask() 时,系统会根据当前的池状态来判断该线程是否需要退出。如果需要退出,线程会完成当前的任务并退出,线程会自然销毁,资源会被 JVM 自动回收。这种方式避免了显式地调用线程销毁方法,而是通过线程自然结束的方式来管理线程池中的线程生命周期。

posted @ 2025-04-05 09:56  零1零1  阅读(72)  评论(0)    收藏  举报