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 分钟前
XP系统下用mod_jk 1.2.40整合apache2.2.16和tomcat 6.0.29,让apache可以同时访问php和jsp页面
java·tomcat·apache·mod_jk
码上一元2 小时前
SpringBoot自动装配原理解析
java·spring boot·后端
计算机-秋大田2 小时前
基于微信小程序的养老院管理系统的设计与实现,LW+源码+讲解
java·spring boot·微信小程序·小程序·vue
魔道不误砍柴功4 小时前
简单叙述 Spring Boot 启动过程
java·数据库·spring boot
失落的香蕉4 小时前
C语言串讲-2之指针和结构体
java·c语言·开发语言
枫叶_v4 小时前
【SpringBoot】22 Txt、Csv文件的读取和写入
java·spring boot·后端
wclass-zhengge4 小时前
SpringCloud篇(配置中心 - Nacos)
java·spring·spring cloud
路在脚下@4 小时前
Springboot 的Servlet Web 应用、响应式 Web 应用(Reactive)以及非 Web 应用(None)的特点和适用场景
java·spring boot·servlet
黑马师兄4 小时前
SpringBoot
java·spring
数据小小爬虫4 小时前
如何用Java爬虫“偷窥”淘宝商品类目API的返回值
java·爬虫·php