Java 线程池

线程的创建和销毁是昂贵的操作,会消耗大量的系统资源。线程池任务队列执行完成后会保留 corePoolSize 数量的线程,以备重复使用。这降低了线程创建和销毁的开销,提高了系统的性能和响应时间。

ThreadPoolExecutor

构造函数

线程池构造函数参数

  • corePoolSize : 核心线程数量 ,线程池里面会一直保留,没有任务时休眠,有任务时唤醒
  • maximumPoolSize : 最大线程数量 ,当核心线程数量不够完成任务,而外开启新线程进行工作,整个线程池的线程总数。
  • keepAliveTime : 当新建线程超过了核心线程数,当没有任务时,非核心线程存活多久进行才销毁线程
  • unit : keepAliveTime 的时间单位
  • workQueue : 任务队列,当线程来不及处理任务时,存储在此队列里 这是一个阻塞队列的数据结构 关于阻塞队列 Java 队列介绍
  • threadFactory : 线程工厂
  • handler : 拒绝策略,当线程池线程数量已经到达最大值 maximumPoolSize ,且当 workQueue 任务队列无法添加任务时,如果继续再向线程池中添加任务时,决定线程池的操作策略。

线程工厂

线程池创建时线程时会调用 newThread() 方法 ,用于线程池创建线程时对线程进行统一配置 ,如给线程设置线程名,优先级等

java 复制代码
public interface ThreadFactory {
    Thread newThread(Runnable r);
}

拒绝策略

  • CallerRunsPolicy : 如果线程池没有停止,就在当前添加任务到线程池的线程直接运行
  • AbortPolicy : 抛出 RejectedExecutionException 异常,线程池默认策略
  • DiscardPolicy : 直接废弃任务,当什么都没发生
  • DiscardOldestPolicy : 如果线程池没有停止,把任务队列里面一个老的任务删除,新任务添加上去
java 复制代码
public interface RejectedExecutionHandler {
    // r 为添加的 Runnable, executor 如当前线程池
    void rejectedExecution(Runnable r, ThreadPoolExecutor executor);
}

线程池的状态

线程池使用一个 int 类型参数的前 3 位表示线程池的状态,后 29 位表示线程池的线程数量。并且通过原子类 AtomicInteger 包装,ctl 初始化为 RUNNING 状态,线程数量为 0.

通过打印成二机制,理解起来会更清楚。

arduino 复制代码
/* 计算后的数值 二进制表示
COUNT_MASK : 00011111111111111111111111111111
RUNNING    : 11100000000000000000000000000000
SHUTDOWN   : 00000000000000000000000000000000
STOP       : 00100000000000000000000000000000
TIDYING    : 01000000000000000000000000000000
TERMINATED : 01100000000000000000000000000000
*/

线程池状态只看前 3位,

  • 只有 RUNNING 为负数,
  • SHUTDOWN 为 0.
  • STOP,TIDYING,TERMINATED 是正数的最高位 所以可以通过数值计算进行状态比较。

如 小于 SHUTDOWN 的只有 RUNNING ,通过这个能够判断线程池正在运行。

java 复制代码
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
private static final int COUNT_BITS = Integer.SIZE - 3;
private static final int COUNT_MASK = (1 << COUNT_BITS) - 1;

// 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;


// 获取线程数量,只取后面 29位即可
private static int workerCountOf(int c)  { return c & COUNT_MASK; }
private static int ctlOf(int rs, int wc) { return rs | wc; }


private static boolean runStateLessThan(int c, int s) {
    return c < s;
}

private static boolean runStateAtLeast(int c, int s) {
    return c >= s;
}

private static boolean isRunning(int c) {
    return c < SHUTDOWN;
}

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler) {
    // 参数正确性校验                      
    if (corePoolSize < 0 ||
        maximumPoolSize <= 0 ||
        maximumPoolSize < corePoolSize ||
        keepAliveTime < 0)
        throw new IllegalArgumentException();
    if (workQueue == null || threadFactory == null || handler == null)
        throw new NullPointerException();
    this.corePoolSize = corePoolSize;
    this.maximumPoolSize = maximumPoolSize;
    this.workQueue = workQueue;
    this.keepAliveTime = unit.toNanos(keepAliveTime);
    this.threadFactory = threadFactory;
    this.handler = handler;
}
  • RUNNING: 接收新的任务,并且处理入队的任务
  • SHUTDOWN: 不接收新的任务但是处理已经入队的任务
  • STOP: 不接收新的任务,也不处理入队的任务,中断正在处理任务的线程
  • TIDYING: 当所有的任务已被停止 ,线程数量为 0。线程池的状态为 TIDYING 然后会执行 terminated() 方法 ,terminated() 方法执行完成,状态会转移到 TERMINATED
  • TERMINATED: 线程池完全停止状态
--- title: 线程池状态时序图 --- sequenceDiagram RUNNING->>SHUTDOWN: shutdown() RUNNING->>STOP: shutdownNow() SHUTDOWN->>TIDYING: 当线程池中所有线程已被终止并且任务队列为空 STOP->>TIDYING: 当线程池中所有线程已被终止并且任务队列为空 TIDYING->>TERMINATED: 当 terminated() 执行完成

把任务添加到线程池进行执行

java 复制代码
public void execute(Runnable command) {
    // 任务为空直接抛异常 
    if (command == null)
        throw new NullPointerException();
    // 获取线程池当前状态
    int c = ctl.get();
    // 如果线程个数小于核心线程数量,直接调用 addWorker 开启新核心线程
    if (workerCountOf(c) < corePoolSize) {
        // 开启新线程成功直接返回 ,注意 addWorker 参数 command 不为 null ,第二个参数为 true 表示核心线程
        if (addWorker(command, true))
            return;
        c = ctl.get();
    }
    // 线程池处于 RUNNING 状态 ,并且任务队列添加任务成功  
    if (isRunning(c) && workQueue.offer(command)) {
        int recheck = ctl.get(); // 再次获取线程池状态 
        // 线程池不是 RUNNING 状态,并且移除成功 remove 的时候会尝试终止线程池 
        if (! isRunning(recheck) && remove(command))
            reject(command); // 拒绝执行任务 
         // 线程池正在运行中,核心线程设置为 0 ,command 已加入到任务队列 
         // 当线程池运行线程数量为 0 时 , 需要开启一个线程进行处理刚添加到任务队列的 command 
        else if (workerCountOf(recheck) == 0) 
            addWorker(null, false); // 开启一个没有初始运行任务的非核心线程 
    }
    else if (!addWorker(command, false)) // 无法添加,可能意味着任务队列达到上限,开启非核心线程 
        reject(command); // 非核心线程无法创建时,拒绝执行任务 
}

创建线程

java 复制代码
/*
  修改ctl失败时 需要重新加在 ctl的值 。有两层无限循环进行检查
  第一层无限循环 有 retry 标记,检查了线程池的状态,是否停止
  第二层无限循环 检查的是线程池数量是否超过了设定值
*/
// firstTask 代表新建线程的第一个任务,core 代表是否核心线程
private boolean addWorker(Runnable firstTask, boolean core) {
    // retry 标记,用于跳出多重循环
    // break continue 加 retry 代表操作的是 retry: 紧接着的for循环 
    retry:
    for (int c = ctl.get();;) {
        // 大于等于 SHUTDOWN ,只能是 SHUTDOWN STOP,TIDYING,TERMINATED 这几种
        // 当线程池状态是 SHUTDOWN 时 ,firstTask 为 null,并且任务队列不为空,可能才会开启线程进行完成任务队列中已添加的任务 
        // 当线程池状态是 STOP,TIDYING,TERMINATED 时直接返回 false ,不会开启新线程。
        if (runStateAtLeast(c, SHUTDOWN)
            && (runStateAtLeast(c, STOP)
                || firstTask != null
                || workQueue.isEmpty()))
            return false;

        for (;;) {
            
            // 请注意:我们初始化线程参数的时候并没有校验 corePoolSize,maximumPoolSize 数值最大值范围,可能超过29位,
            // 所以必须 与上 COUNT_MASK 去除前3位 
            // 判断线程数量是否超过核心线程数或者最大线程数,超过了直接返回 false
            // 没有超过的话,进行新开线程。
            if (workerCountOf(c)
                >= ((core ? corePoolSize : maximumPoolSize) & COUNT_MASK))
                return false;
            // 线程数量加 1成功 ,跳出 retry 循环
            if (compareAndIncrementWorkerCount(c))
                break retry;
            // 原子类加 1 失败,说明其他线程已更改 ctl 值 重新获取    
            c = ctl.get();  // Re-read ctl
            //当线程池状态大于等于 SHUTDOWN compareAndIncrementWorkerCount CAS失败
            // 是 由于线程池状态发生了变化,直接跳到第一层循环
            if (runStateAtLeast(c, SHUTDOWN))
                continue retry;
            // 否则 compareAndIncrementWorkerCount CAS失败 由于线程数量发生了变化,重试第二层循环
        }
    }
    // 通过了线程池的状态和数量检查,进行开启线程 
    boolean workerStarted = false;
    boolean workerAdded = false;
    Worker w = null;
    try {
    // 创建 Worker 对象,构造函数里面会调用 ThreadFactory.newThread()方法创建线程
        w = new Worker(firstTask); 
        final Thread t = w.thread;
        if (t != null) {
            final ReentrantLock mainLock = this.mainLock;
            mainLock.lock();
            try {
                int c = ctl.get();
                if (isRunning(c) ||
                    (runStateLessThan(c, STOP) && firstTask == null)) {
                    // 如何线程不是刚创建的线程,抛出异常
                    if (t.getState() != Thread.State.NEW)
                        throw new IllegalThreadStateException();
                    workers.add(w); // 把 w 添加到集合中
                    workerAdded = true;
                    int s = workers.size();
                    if (s > largestPoolSize) // 记录线程池中开的最大线程数
                        largestPoolSize = s;
                }
            } finally {
                mainLock.unlock();
            }
            // 如果 worker添加了,进行启动线程
            if (workerAdded) {
                t.start(); // 启动线程会执行,Worker 的 run 方法
                workerStarted = true;
            }
        }
    } finally {
        // 如果未启动,调用 addWorkerFailed 方法
        if (! workerStarted)
            addWorkerFailed(w);
    }
    // 返回启动状态
    return workerStarted;
}

// 如果创建线程失败
private void addWorkerFailed(Worker w) {
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        if (w != null)
            workers.remove(w); // 移除启动失败的线程 
        decrementWorkerCount();  // 线程池数量 -1
        tryTerminate(); // 尝试终止线程池 
    } finally {
        mainLock.unlock();
    }
}

执行线程

Worker 继承 了 AbstractQueuedSynchronizer

java 复制代码
private final class Worker
    extends AbstractQueuedSynchronizer
    implements Runnable
{
    final Thread thread;
    Runnable firstTask;
    volatile long completedTasks;

    Worker(Runnable firstTask) {
        setState(-1); // inhibit interrupts until runWorker
        this.firstTask = firstTask;
        this.thread = getThreadFactory().newThread(this);
    }

   // 当启动线程时会调用此方法
    public void run() {
        runWorker(this);
    }
    // 0 代表未被锁定状态
    // 1 代表被锁定状态
    protected boolean isHeldExclusively() {
        return getState() != 0;
    }

    protected boolean tryAcquire(int unused) {
        if (compareAndSetState(0, 1)) {
            setExclusiveOwnerThread(Thread.currentThread());
            return true;
        }
        return false;
    }

    protected boolean tryRelease(int unused) {
        setExclusiveOwnerThread(null);
        setState(0);
        return true;
    }

    public void lock()        { acquire(1); }
    public boolean tryLock()  { return tryAcquire(1); }
    public void unlock()      { release(1); }
    public boolean isLocked() { return isHeldExclusively(); }

    void interruptIfStarted() {
        Thread t;
        if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
            try {
                t.interrupt();
            } catch (SecurityException ignore) {
            }
        }
    }
}

final void runWorker(Worker w) {
    Thread wt = Thread.currentThread();
    Runnable task = w.firstTask;
    w.firstTask = null;
    w.unlock(); // allow interrupts
    boolean completedAbruptly = true;  // 线程是否异常退出 
    try {
       // task 为 firstTask ,通过 getTask()去任务队列中拿 task ,
       // 如果 task 为空会退出 while 循环 ,线程会终止 。 
        while (task != null || (task = getTask()) != null) {
            w.lock();
            
            if ((runStateAtLeast(ctl.get(), STOP) ||
                 (Thread.interrupted() &&
                  runStateAtLeast(ctl.get(), STOP))) &&
                !wt.isInterrupted())
                wt.interrupt();
            try {
                beforeExecute(wt, task); // 任务执行之前 
                try {
                    task.run(); // 开始执行任务 
                    afterExecute(task, null); // 任务执行之后 
                } catch (Throwable ex) {
                    afterExecute(task, ex);
                    throw ex;
                }
            } finally {
                task = null;  // task 设置为 null
                w.completedTasks++; // 记录线程完成的 completedTasks 
                w.unlock(); 
            }
        }
        completedAbruptly = false; // 非异常退出 
    } finally {
        // 处理线程退出 
        processWorkerExit(w, completedAbruptly);
    }
}

// 最大线程数,核心线程数,是否允许核心线程超时 都是可以动态设置的
// 获取任务,当返回的task 为 null,就可以正常退出线程  
private Runnable getTask() {
    boolean timedOut = false; // Did the last poll() time out?

    for (;;) {
        int c = ctl.get();

         // 大于等于 SHUTDOWN ,只能是 SHUTDOWN STOP,TIDYING,TERMINATED 这几种
        // 当线程池状态是 SHUTDOWN 时 ,并且任务队列为空 返回 null 
        // 当线程池状态是 STOP,TIDYING,TERMINATED 时直接返回 null
        if (runStateAtLeast(c, SHUTDOWN)
            && (runStateAtLeast(c, STOP) || workQueue.isEmpty())) {
            decrementWorkerCount(); // 返回 null 之前 ,线程数 -1 
            return null; // 返回 null 代表线程是 正常退出的 
        }

        int wc = workerCountOf(c); // 获取线程数 
       
        // 允许核心线程超时,或者当前线程数大于核心线程数 
        boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
        // 1.当前线程数大于最大线程数 
        // 2.当前线程数大于核心线程数,并且任务队列为空 
        // 3.核心线程运行超时,当前已经超时 ,并且任务队列为空   
        if ((wc > maximumPoolSize || (timed && timedOut))
            && (wc > 1 || workQueue.isEmpty())) {
            if (compareAndDecrementWorkerCount(c)) // 线程数 -1 ,返回 null
                return null;
            continue;
        }

        try {
            Runnable r = timed ? // 如果允许核心线程超时,任务队列没有任务会等待 keepAliveTime 时间 
                workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                workQueue.take(); // 如果任务队列没有任务会一直阻塞等待 
            if (r != null) // 如果拿到的 任务 ,返回 r 
                return r;
            timedOut = true; // 如果 r 为 null ,说明超时 
        } catch (InterruptedException retry) {
            timedOut = false; // 或者 等待任务队列时,被 interrupt() ,没有超时 

        }
    }
}


private void processWorkerExit(Worker w, boolean completedAbruptly) {
    if (completedAbruptly) // 异常退出 ,统计线程数 -1,正常退出 ,已经减过 1   
        decrementWorkerCount();

    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        completedTaskCount += w.completedTasks; // 统计已完成的任务数 
        workers.remove(w); // 去除 Worker
    } finally {
        mainLock.unlock();
    }
    // 尝试终止线程池 
    tryTerminate();

    int c = ctl.get();
    // 线程池状态为 RUNNING,SHUTDOWN 
    if (runStateLessThan(c, STOP)) {
        // 正常退出
        if (!completedAbruptly) {
            // 运行核心线程超时 min 为 0 ,否则为 corePoolSize
            int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
            // 当 min 为 0 ,任务队列不为空 
            if (min == 0 && ! workQueue.isEmpty())
                min = 1; 
            // 如果当前线程池还是其他线程存活 直接返回
            if (workerCountOf(c) >= min)
                return; // replacement not needed
        }
        // 当前线程池状态为 SHUTDOWN ,线程池没有其他线程 ,并且任务队列不为空 
        // 开启一个线程来执行剩下未完成的任务 
        addWorker(null, false);
    }
}

// 尝试终止线程池 
final void tryTerminate() {
    for (;;) {
        int c = ctl.get();
        // 当以下几种情况下 直接返回,不更改线程池状态 
        // 1. 线程池状态为 RUNNING
        // 2. 线程池状态为 TIDYING ,TERMINATED 
        // 3. 线程池状态为 SHUTDOWN 并且任务队列不为空 
        if (isRunning(c) ||
            runStateAtLeast(c, TIDYING) ||
            (runStateLessThan(c, STOP) && ! workQueue.isEmpty()))
            return;
        // 线程数不为 0 时 中止空闲的线程 直接返回,不更改线程池状态     
        if (workerCountOf(c) != 0) { // Eligible to terminate
            interruptIdleWorkers(ONLY_ONE);
            return;
        }

        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            // 当前线程池状态设置为 TIDYING
            if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {
                try {
                    terminated(); // 执行 terminated() 方法 
                } finally {
                    ctl.set(ctlOf(TERMINATED, 0));// 当前线程池状态设置为 TERMINATED
                    termination.signalAll(); // 通知等待线程池终止的线程线程已终止   
                }
                return;
            }
        } finally {
            mainLock.unlock();
        }
        // else retry on failed CAS
    }
}

停止线程池

java 复制代码
// 停止线程池,但是有不会清除任务队列,等待任务队列执行完成 
public void shutdown() {
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        checkShutdownAccess();
        advanceRunState(SHUTDOWN); // 把线程池状态置为 SHUTDOWN
        interruptIdleWorkers(); // 中止空闲的线程 
        onShutdown(); // hook for ScheduledThreadPoolExecutor 
    } finally {
        mainLock.unlock();
    }
    tryTerminate(); // 尝试停止线程池 
}

// 立即停止线程池,未完成的任务列表会被清除并返回 
public List<Runnable> shutdownNow() {
    List<Runnable> tasks;
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        checkShutdownAccess();
        advanceRunState(STOP); // 把线程池状态置为 STOP
        interruptWorkers(); // 中止所有的线程 
        tasks = drainQueue(); // 移除未完成的任务队列
    } finally {
        mainLock.unlock();
    }
    tryTerminate(); // 尝试停止线程池 
    return tasks;
}


// 设置线程池的状态 
private void advanceRunState(int targetState) {
    // assert targetState == SHUTDOWN || targetState == STOP;
    for (;;) {
        int c = ctl.get();
        if (runStateAtLeast(c, targetState) ||
            ctl.compareAndSet(c, ctlOf(targetState, workerCountOf(c))))
            break;
    }
}

private void interruptIdleWorkers() {
    interruptIdleWorkers(false);
}

private void interruptIdleWorkers(boolean onlyOne) {
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        for (Worker w : workers) {
            Thread t = w.thread;
            // 没有调用过 interrupt(),并且能够获取锁 ,说明在getTask()方法中等待获取任务队列的任务 
            if (!t.isInterrupted() && w.tryLock()) { 
                try {
                    t.interrupt(); // 调用 interrupt() 会抛出 InterruptedException 
                } catch (SecurityException ignore) {
                } finally {
                    w.unlock();
                }
            }
            if (onlyOne)
                break;
        }
    } finally {
        mainLock.unlock();
    }
}


private void interruptWorkers() {
    // assert mainLock.isHeldByCurrentThread();
    for (Worker w : workers)
        w.interruptIfStarted(); // 只要线程启动了 调用 interrupt()
}

Executors

Executors 工具类提供了几个静态方法快速创建各种类型的线程池。

  • newCachedThreadPool()
    • 创建一个 0 核心线程数 ,最大线程数为 Integer.MAX_VALUE ,没有任务时线程保持存活 60 秒。 SynchronousQueue<Runnable>()是一个零容量的队列,不会保存元素用于后续的访问,当一个线程试图向中插入元素时,它会被阻塞,直到另一个线程从队列中取走这个元素,同样地,当一个线程试图从 中取走元素时,它也会被阻塞,直到另一个线程将元素放入队列中 。当有大量任务耗时任务添加到此线程池时,会创建大量线程,不会限制创建线程的数量,会消耗很多系统资源,甚至OOM 需要特别注意
  • newSingleThreadScheduledExecutor()
    • 创建单核心线程数为 1 ,最大线程数也为 1 ,阻塞队列 LinkedBlockingQueue<Runnable>() 可以保存 Integer.MAX_VALUE 任务 ,当有大量任务耗时任务添加到此线程池时,需要注意任务队列 OOM
  • newFixedThreadPool(int nThreads)
    • 创建固定线程数量的线程池 ,单核心线程数为 nThreads ,最大线程数也为 nThreads ,阻塞队列 也是 LinkedBlockingQueue<Runnable>() 需要注意任务队列 OOM
  • newScheduledThreadPool()
    • 创建 ScheduledExecutorService ,可设置定时任务,周期任务的线程池 。阻塞队列DelayedWorkQueue() 也是可以保存Integer.MAX_VALUE 任务 ,当有大量任务耗时任务添加到此线程池时,需要注意任务队列 OOM
相关推荐
砚底藏山河5 分钟前
python量化实操:回测数据缓存与重放
java·python·金融·1024程序员节
学长毕设10 分钟前
基于springboot的农资管理系统-附源码
java·spring boot·后端
晒太羊的猫27 分钟前
hot100子串
java·算法·leetcode
开开心心就好1 小时前
本地文件搜索工具,双击Ctrl就能呼出搜索
java·服务器·前端·支持向量机·ffmpeg·音视频·模拟退火算法
独隅1 小时前
Claude 高效使用指南深度评测
java·开发语言·claude·claude code
ByteCoder.1 小时前
班前十分钟的清单,按体检引导单来写
java·服务器·前端·助播虾
海的透彻1 小时前
如何让docker容器启动时用自定义的用户
java·docker·dubbo
疯狂学习GIS1 小时前
将谷歌地球引擎账号升级为Contributor与Partner层级的方法
java·数据库·人工智能
GG.WinGel1 小时前
质数数目(蓝桥杯练习题)
java·数据结构·算法·青少年编程·蓝桥杯