源码分析:深入分析 Java ThreadPoolExecutor:原理与源码解析

94 阅读3分钟

ThreadPoolExecutor是什么?

ThreadPoolExecutor 是 Java 中用于管理线程池的类之一。它提供了一个灵活的线程池实现,可以根据需要自动管理线程的创建、销毁和重用,以及控制并发任务的执行。

应用场景

  1. 1. 线程生命周期管理
    • ThreadPoolExecutor 负责管理线程的生命周期,包括线程的创建、销毁和重用。通过线程池,可以避免频繁地创建和销毁线程,减少资源消耗和系统开销。
  2. 2. 任务调度与执行
    • ThreadPoolExecutor 负责调度和执行提交给线程池的任务。可以将任务提交给线程池执行,并根据需要调整线程池的大小,以适应不同的工作负载和系统资源。
  3. 3. 并发控制
    • ThreadPoolExecutor 提供了一种并发控制的机制,可以限制线程池中同时执行的任务数量,以避免系统资源耗尽或者任务堆积导致的性能问题。
  4. 4. 性能优化
    • • 通过合理地配置线程池参数,可以优化任务的执行效率和系统的资源利用率,提高程序的性能和响应速度。

ThreadPoolExecutor工作原理

类层次结构

可以看到ThreadPoolExecutor是继承自接口ExecutorService的实现类,ThreadPoolExecutor的上层抽象接口实现了submit、invoke等方法,在ThreadPoolExecutor中实现了模板方法中的execute方法

执行过程

  1. 1. 获取当前工作的线程运行状态控制

  2. 2. 比较运行中的线程数和初始化时的核心线程数

  3. 3. 如果小于核心线程数,则创建新的线程来处理任务

  4. 4. 如果等待队列未满,则加入等待队列

  5. 5. 否则判断是否超过了核心线程数,未超过则新增线程来处理任务

  6. 6. 否则执行拒绝策略

    public void execute(Runnable command) { if (command == null) throw new NullPointerException(); /* * Proceed in 3 steps: * * 1. If fewer than corePoolSize threads are running, try to * start a new thread with the given command as its first * task. The call to addWorker atomically checks runState and * workerCount, and so prevents false alarms that would add * threads when it shouldn't, by returning false. * * 2. If a task can be successfully queued, then we still need * to double-check whether we should have added a thread * (because existing ones died since last checking) or that * the pool shut down since entry into this method. So we * recheck state and if necessary roll back the enqueuing if * stopped, or start a new thread if there are none. * * 3. If we cannot queue task, then we try to add a new * thread. If it fails, we know we are shut down or saturated * and so reject the task. */ 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. 1. 核心线程数:真正执行任务的线程数,一般设置为CPU核数+1,如果是IO密集型可以适当增加
  2. 2. 最大线程数:允许创建的最大线程数,一般设置等于核心线程数,因为超过了核心线程数,再创建新的线程,只会产生新的上下文切换和资源竞争的开销,并不能提升线程池处理任务的速度
  3. 3. 等待超时时间: 设定一个线程的执行超时时间,超过这个时间还未执行完,则会释放当前线程资源
  4. 4. 等待队列: 用于留存等待处理的任务

ThreadPoolExecutor 创建方式

  1. 1. 通过ThreadPoolExecutor给定的构造方法
  2. 2. 通过Executors提供的静态方法创建,Executors目前提供了newFixedThreadPool、newWorkStealingPool、newCachedThreadPool:用来处理短期任务,在60秒内会重用已创建的线程执行任务

建议通过ThreadPoolExecutor的构造方法手动创建

Executors创建的线程池

  1. 1. newFixedThreadPool:固定线程数的线程池,使用LinkedBlockingQueue作为无界的等待队列,可能出现因任务等待过多导致OOM
  2. 2. newWorkStealingPool:通过ForkJoinPool实现的线程池,具备工作窃取的能力(1.8以后开始提供)
  3. 3. newCachedThreadPool:用来处理短期任务,在60秒内会重用已创建的线程执行任务
  4. 4. newSingleThreadScheduledExecutor:用来延迟调度的线程池

注意点

  1. 1. 最大线程数和核心线程数并不是越大越好,需要根据服务器配置以及任务类型合理配置
  2. 2. 设定一个合理的等待超时时间,有助于快速处理任务
  3. 3. 等待队列需要考虑OOM相关问题,例如不要用无界队列,避免因创建过多任务导致OOM问题

应用示例

如下所示是一个计算数组求和的任务,分别通过ForkJoinPool和ThreadPoolExecutor实现

ThreadPoolExecutor实现

import org.springframework.util.StopWatch;import java.util.concurrent.*;class MyTask implements Callable<Integer> {    private int[] array;    private int start;    private int end;    public MyTask(int[] array, int start, int end) {        this.array = array;        this.start = start;        this.end = end;    }    // 重写 call 方法来实现任务的具体逻辑    @Override    public Integer call() throws Exception {        int sum = 0;        for (int i = start; i < end; i++) {            sum += array[i];        }        return sum;    }}public class ThreadPoolExecutorTest {    // 实现 Callable 接口来表示可并行执行的任务    public static void main(String[] args) {        int[] array = new int[10000000]; // 假设有一个大数组        StopWatch stopWatch = new StopWatch();        MyTask myTask = new MyTask(array, 0, array.length);        ExecutorService executor = new ThreadPoolExecutor(4, 16, 0, TimeUnit.SECONDS, new ArrayBlockingQueue<>(1000));        stopWatch.start("ThreadPoolExecutor");        Future<Integer> future = executor.submit(myTask);        try {            future.get();        } catch (InterruptedException e) {            throw new RuntimeException(e);        } catch (ExecutionException e) {            throw new RuntimeException(e);        }        stopWatch.stop();        System.out.println(stopWatch.prettyPrint());    }}

总结

总的来说,ThreadPoolExecutor 是一个功能强大、灵活可配置的线程池实现,被广泛应用于 Java 并发编程中,用于管理线程的生命周期、调度任务的执行、优化系统的性能和资源利用率