并发编程之 Executor 线程池原理与源码解读

本文详细剖析了Java ThreadPoolExecutor的工作流程、状态转换、关键方法源码,包括execute()、Worker类、getTask()和addWorker()等,同时介绍了不同拒绝策略。讨论了线程池参数设置和常见技术应用,如CPU密集与IO密集场景的最佳线程数计算。
摘要由CSDN通过智能技术生成

并发编程之 Executor 线程池原理与源码解读

线程是调度 CPU 资源的最小单位,线程模型分为 KLT 模型与 ULT 模型,JVM使用的是 KLT 模型。java线程与 OS 线程保持 1:1 的映射关系,也就是说有一个 java 线程也会在操作系统里有一个对应的线程。java 线程有多种生命状态:

  • NEW 新建
  • RUNNABLE 运行
  • BLOCKED 阻塞
  • WAITING 等待
  • TIMED_WAITING 超时等待
  • TERMINAIED 终结

1. ThreadPoolExecutor

ThreadPoolExecutor 构造方法

public ThreadPoolExecutor(
    int corePoolSize,		// 核心线程数
    int maximumPoolSize,	// 最大线程数 = 核心线程数 + 非核心线程数
    long keepAliveTime,		// 最大允许线程不干活的时间
    TimeUnit unit,			// 时间单位
    BlockingQueue<Runnable> workQueue,	// 存放未来得及执行的任务
    ThreadFactory threadFactory,		// 创建线程的工厂
    RejectedExecutionHandler handler	// 拒绝策略
) {...}

线程本身没有标记核心和非核心,只是用线程数量进行区分

1.1 线程池运行的流程图

在这里插入图片描述

1.2 线程池的状态

// runState is stored in the high-order bits
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;
RUNNING

(1)状态说明:线程池处在 RUNNING 状态时, 能够接收新任务,以及对已添加的任务进行处理

(2)状态切换:线程池的初始化状态是 RUNNING。换句话说,线程一旦被创建,就处于 RUNNING 状态,并且线程池中的任务数为 0 !

SHUDOWN

(1)状态说明:线程池处在 SHUTDOWN 状态时,不接收新任务,但能够处理已添加的任务

(2)状态切换:调用线程池的 shutdown() 接口时,线程池由 RUNNING -> SHUTDOWN

STOP

(1)状态说明:线程池处在 STOP 状态时,不接收新任务,不处理已添加的任务,并且会中断正在处理的任务

(2)状态切换:调用线程池的shutdownNow() 接口时,线程池由(RUNNING or SHUTDOWN)->STOP

TIDYING

(1)状态说明:当所有的任务已终止,ctl 记录的 “任务数量” 为 0,线程池会变为 TIDYING 状态。当线程池变为 TIDYING 状态时,会执行钩子函数 terminated() 。terminated() 再 ThreadPoolExecutor 类中是空的,若用户想在线程池变为 TIDYING 时,进行相应的处理;可以通过重载 terminated() 函数来实现。

(2)状态切换:当线程池在 SHUTDOWN 状态下,阻塞队列为空并且线程池中执行的任务也为空时,就会由 SHUTDOWN -> TIDYING。当线程池在 STOP 状态下,线程池中执行任务为空时,就会由 STOP -> TIDYING

TERMINATED

(1)状态说明:线程池彻底终止,就变成 TERMINATED 状态

(2)状态切换:线程池处在 TIDYING 状态时,执行完 terminated() 之后,就会由 TIDYING -> TERMINATED

进入 TERMINATED 的条件如下:

  • 线程池不是 RUNNING 状态
  • 线程池状态不是 TIDYING 状态或 TERMINATED 状态
  • 如果线程池状态是 SHUTDOWN 并且 workerQueue 为空
  • workerCount 为 0
  • 设置 TIDYING 状态成功

在这里插入图片描述

1.3 重要方法源码解读

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

    int c = ctl.get();
    // 1. 如果工作的线程数 < 核心线程数,创建核心线程
    if (workerCountOf(c) < corePoolSize) {
        // true 表示要创建的线程为核心线程,如果创建成功则返回
        if (addWorker(command, true))
            return;
        // 核心线程创建失败,重新获取 c 的状态
        c = ctl.get();
    }
	// 2. 如果线程池没有关闭,即线程池正在运行,那么将当前任务添加到阻塞队列
    if (isRunning(c) && workQueue.offer(command)) {
        int recheck = ctl.get();
        // 再次检查,如果线程池已关闭,那么就移除任务
        if (! isRunning(recheck) && remove(command))
            // 拒绝任务
            reject(command);
        // 如果线程池的状态是 shutdown 状态
        else if (workerCountOf(recheck) == 0)
            addWorker(null, false);
    }
    // 3. 创建非核心线程,如果非核心线程数达到上限,触发拒绝策略
    else if (!addWorker(command, false))
        reject(command);
}
1.3.2 Worker 类

worker 是继承 AbstractQueuedSynchronizer 类,且实现了 Runnable 接口

private final class Worker
        extends AbstractQueuedSynchronizer
        implements Runnable {...}
成员变量
final Thread thread;
/** Initial task to run.  Possibly null. */
Runnable firstTask;
/** Per-thread task counter */
volatile long completedTasks;
构造方法
Worker(Runnable firstTask) {
    setState(-1); // inhibit interrupts until runWorker
    this.firstTask = firstTask;
    // 使用线程工厂,创建一个新的线程,并将这个 worker 自己作为任务丢给新创建的线程
    this.thread = getThreadFactory().newThread(this);
}
run() 方法

内部类 Worker 的 run() 方法调用外部类方法 runWorker()

public void run() {
    runWorker(this);
}
1.3.3 runWorker() 方法(为什么线程可以重用)
final void runWorker(Worker w) {
    Thread wt = Thread.currentThread();
    Runnable task = w.firstTask;
    w.firstTask = null;
    
    // 线程刚刚创建,正在创建不允许中断 
    w.unlock(); // allow interrupts
    
    boolean completedAbruptly = true;
    try {
        // 为什么线程池可以重用?	循环 + 条件
        // 线程超时处理?
        while (task != null || (task = getTask()) != null) {
            w.lock();
            // If pool is stopping, ensure thread is interrupted;
            // if not, ensure thread is not interrupted.  This
            // requires a recheck in second case to deal with
            // shutdownNow race while clearing interrupt
            if ((runStateAtLeast(ctl.get(), STOP) ||
                 (Thread.interrupted() &&
                  runStateAtLeast(ctl.get(), STOP))) &&
                !wt.isInterrupted())
                wt.interrupt();
            try {
                beforeExecute(wt, task);
                Throwable thrown = null;
                try {
                    task.run();
                } catch (RuntimeException x) {
                    thrown = x; throw x;
                } catch (Error x) {
                    thrown = x; throw x;
                } catch (Throwable x) {
                    thrown = x; throw new Error(x);
                } finally {
                    afterExecute(task, thrown);
                }
            } finally {
                task = null;
                w.completedTasks++;
                w.unlock();
            }
        }
        // 出现 Exception 时,该行不会执行
        completedAbruptly = false;
    } finally {
        // completedAbruptly = true
        processWorkerExit(w, completedAbruptly);
    }
}

如果任务执行出现异常,异常会往外抛出,同时执行如上两个 finally 代码块。第一个 finally 释放锁;第二个 finally清理掉异常任务,并调用 addWorker(null,false)方法,继续从队列中取出下一个任务执行。

private void processWorkerExit(Worker w, boolean completedAbruptly) {
    
    // completedAbruptly = true 表明有线程因抛出异常而挂掉,总的线程数量需要减去 1
    if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
        decrementWorkerCount();

    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        completedTaskCount += w.completedTasks;
        // 从 workers 集合中移除执行完毕的任务(包含挂掉的线程任务)
        workers.remove(w);
    } finally {
        mainLock.unlock();
    }

    // 如果符合终止条件,尝试终止线程(清理掉空闲线程)
    tryTerminate();

    int c = ctl.get();
    // 判断当前线程池的生命状态为 RUNNING 或 SHUTDOWN,继续从队列获取任务执行
    if (runStateLessThan(c, STOP)) {
        if (!completedAbruptly) {
            int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
            if (min == 0 && ! workQueue.isEmpty())
                min = 1;
            if (workerCountOf(c) >= min)
                return; // replacement not needed
        }
        // 继续获取队列中的一下个任务去执行
        addWorker(null, false);
    }
}
1.3.4 getTask() 方法
allowCoreThreadTimeOut
  • true 标识核心线程也会过期,过期的核心线程将会被清理(需手动设置)
  • false (默认)标识核心线程不会过期
private Runnable getTask() {
    boolean timedOut = false; // Did the last poll() time out?

    for (;;) {
        // 状态判断
        int c = ctl.get();
        int rs = runStateOf(c);

        // Check if queue empty only if necessary.
        if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
            decrementWorkerCount();
            return null;
        }

        int wc = workerCountOf(c);

        // Are workers subject to culling?
        // allowCoreThreadTimeOut 设置为 true,那么核心线程也会过期(需手动设置,默认不过期)
        boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;

        if ((wc > maximumPoolSize || (timed && timedOut))
            && (wc > 1 || workQueue.isEmpty())) {
            if (compareAndDecrementWorkerCount(c))
                return null;
            continue;
        }

        // 
        try {
            Runnable r = timed ?
                // timed = true  阻塞队列 当队列为空,条件不满足,被阻塞指定时间!
                // timed = false 阻塞队列 take() 没有时间限制,那么就会一直被阻塞,等在这里
                workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
            workQueue.take();
            if (r != null)
                return r;
            timedOut = true;
        } catch (InterruptedException retry) {
            timedOut = false;
        }
    }
}
1.3.5 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.
        if (rs >= SHUTDOWN &&
            ! (rs == SHUTDOWN &&
               firstTask == null &&
               ! workQueue.isEmpty()))
            return false;

        for (;;) {
            int wc = workerCountOf(c);
            if (wc >= CAPACITY ||
                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 {
        // 创建一个新的 worker,并将第一个任务丢给它
        w = new Worker(firstTask);
        final Thread t = w.thread;
        // 如果 worker 的成员变量 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();
                    
                    // 将 worker 放到 HashSet<Worker> workers 集合中去,如果线程被终结需维持引用
                    workers.add(w);
                    int s = workers.size();
                    if (s > largestPoolSize)
                        largestPoolSize = s;
                    workerAdded = true;
                }
            } finally {
                mainLock.unlock();
            }
            if (workerAdded) {
                // 启动线程,调用的是 Worker 中的 run() 方法
                t.start();
                workerStarted = true;
            }
        }
    } finally {
        if (! workerStarted)
            addWorkerFailed(w);
    }
    return workerStarted;
}

1.4 拒绝策略

如果在任务不允许丢的情况下,可以重写拒绝策略,然后将任务存放在中间件中如 redis。

当阻塞队列已满,就将任务写入redis中,开启一个线程,专门监测阻塞队列使用情况。当阻塞队列占用降低到 50% 时,再将 redis 中存放的任务取出放入阻塞队列中。

在这里插入图片描述

处理拒绝策略的 handler,声明为 volatile 是为了在多线程运行环境下,可能会更改拒绝策略,以便通知到其它线程

private volatile RejectedExecutionHandler handler;

默认的拒绝策略,直接往外抛异常

private static final RejectedExecutionHandler defaultHandler =
        new AbortPolicy();
1.4. 1 AbortPolicy

​ 默认的拒绝策略,直接往外抛异常

public AbortPolicy() { }

public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
    throw new RejectedExecutionException("Task " + r.toString() +
                                         " rejected from " +
                                         e.toString());
}
1.4.2 DiscardOldestPolicy

如果线程池没有关闭,将阻塞队列对头的线程丢弃,将本次的 task 推到队尾排队

public static class DiscardOldestPolicy implements RejectedExecutionHandler {

    public DiscardOldestPolicy() { }

    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        // 如果线程池没有关闭,将老的对头线程丢弃掉,将 task 放到队尾
        if (!e.isShutdown()) {
            e.getQueue().poll();
            e.execute(r);
        }
    }
}
1.4.3 CallerRunsPolicy

如果线程池拒绝执行本次任务,且线程池没有关闭,直接由当前提交的线程 task 自己去执行,不给线程池执行

public static class CallerRunsPolicy implements RejectedExecutionHandler {

    public CallerRunsPolicy() { }

    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        // 如果线程池没有关闭,直接由当前提交的线程 task 自己去执行,不给线程池执行
        if (!e.isShutdown()) {
            r.run();
        }
    }
}
1.4.4 DiscardPolicy
public static class DiscardPolicy implements RejectedExecutionHandler {

    public DiscardPolicy() { }

    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
    }
}

1.5 ThreadPoolExecutor 参数设置

1.5.1 线程数量设置
  • CPU 密集型 CPU 核数 + 1(计算任务较多,CPU 没有等待时间,一直在计算任务)

  • IO 密集型 2 * CPU 核数 + 1(IO读写等待时间长,充分利用 CPU 时间片,等待时间可执行更多的 IO 操作)

rocketMQ、Eureka、Nacos 2 * CPU

1.5.2 最佳线程数

最佳线程数 = CPU 核数 * [1 + ( I/O 耗时 / CPU 耗时 ) ]

2. ScheduledThreadPoolExecutor

2.1 定时线程池的类结构图

在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值