线程池(ThreadPoolExecutor)详解

线程池使用

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;

public class ThreadPoolExecutorTest {

    /**
     * 创建一个完整线程池参数
     * 核心线程数为 10
     * 最大线程数为 20
     * 存活时间为10 分钟
     * 工作队列 LinkedBlockingQueue
     * 线程工厂为默认 DefaultThreadFactory
     * 拒绝策略 为 AbortPolicy (直接抛异常)
     */
    private static ThreadPoolExecutor executor = new ThreadPoolExecutor(10, 20, 10, TimeUnit.MINUTES,
            new LinkedBlockingQueue<Runnable>(), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());


    /**
     * 创建固定大小核心线程数为5 最大线程数为5 没有超时时间 线程池  工作队列 LinkedBlockingQueue
     */
    private static ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(5);


    /**
     * 创建核心线程数为 0 最大线程数  Integer.MAX_VALUE 存活时间为 60秒 该线程可以无限扩展 工作队列使用同步移交SynchronousQueue
     */
    private static ExecutorService cachedThreadPool = Executors.newCachedThreadPool();


    /**
     * 创建 核心数为 1 最大线程数为 1 没有超时时间 工作队列 LinkedBlockingQueue
     */
    private static ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();

    /**
     * 创建核心线程数为5 给定的延迟之后运行任务, 或者定期执行任务的线程池 工作队列 使用延迟队列 DelayedWorkQueue
     */
    private static ScheduledExecutorService newScheduledThreadPool = Executors.newScheduledThreadPool(5);

    public static void main(String[] args) throws ExecutionException, InterruptedException {

        /**
         * 例子1: 没有返回结果的异步任务
         */
        executor.submit(new Runnable() {
            @Override
            public void run() {
                // do something
                System.out.println("没有返回结果的异步任务");
            }
        });

        /**
         * 例子2: 有返回结果的异步任务
         */
        Future<List<String>> future = executor.submit(new Callable<List<String>>() {
            @Override
            public List<String> call() {
                List<String> result = new ArrayList<>();
                result.add("jackJson");
                return result;
            }
        });
        List<String> result = future.get(); // 获取返回结果
        System.out.println("有返回结果的异步任务: " + result);

        /**
         * 例子3:
         * 有延迟的, 周期性执行异步任务
         * 本例子为: 延迟1秒, 每2秒执行1次
         */
        newScheduledThreadPool.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                System.out.println("this is " + Thread.currentThread().getName());
            }

        }, 1, 2, TimeUnit.SECONDS);

        /**
         * 例子4: FutureTask的使用
         */
        Callable<String> task = new Callable<String>() {
            public String call() {
                return "jackJson";
            }
        };
        FutureTask<String> futureTo = new FutureTask<String>(task);
        executor.submit(futureTo);
        System.out.println(futureTo.get());
    }
}

线程池是什么
线程池(Thread Pool)是一种基于池化思想管理线程的工具,经常出现在多线程服务器中,如MySQL。线程过多会带来额外的开销,其中包括创建销毁线程的开销、调度线程的开销等等,同时也降低了计算机的整体性能。线程池维护多个线程,等待监督管理者分配可并发执行的任务。这种做法,一方面避免了处理任务时创建销毁线程开销的代价,另一方面避免了线程数量膨胀导致的过分调度问题,保证了对内核的充分利用。

线程池有哪些优点

  1. 降低资源消耗:通过池化技术重复利用已创建的线程,降低线程创建和销毁造成的损耗。
  2. 提高响应速度:任务到达时,无需等待线程创建即可立即执行。
  3. 提高线程的可管理性:线程是稀缺资源,如果无限制创建,不仅会消耗系统资源,还会因为线程的不合理分布导致资源调度失衡,降低系统的稳定性。使用线程池可以进行统一的分配、调优和监控。
  4. 提供更多更强大的功能:线程池具备可拓展性,允许开发人员向其中增加更多的功能。比如延时定时线程池ScheduledThreadPoolExecutor,就允许任务延期执行或定期执行。

线程池解决什么问题?
线程池解决的核心问题就是资源管理问题。在并发环境下,系统不能够确定在任意时刻中,有多少任务需要执行,有多少资源需要投入。这种不确定性将带来以下若干问题:

  • 频繁申请/销毁资源和调度资源,将带来额外的消耗,可能会非常巨大。
  • 对资源无限申请缺少抑制手段,易引发系统资源耗尽的风险。
  • 系统无法合理管理内部的资源分布,会降低系统的稳定性。

ThreadPoolExecutor 源码解析

线程池工作流程

在这里插入图片描述

  1. 首先检测线程池运行状态,如果不是RUNNING,则直接拒绝,线程池要保证在RUNNING的状态下执行任务。
  2. 如果workerCount < corePoolSize,则创建并启动一个线程来执行新提交的任务。
  3. 如果workerCount >= corePoolSize,且线程池内的阻塞队列未满,则将任务添加到该阻塞队列中。
  4. 如果workerCount >= corePoolSize && workerCount <
    maximumPoolSize,且线程池内的阻塞队列已满,则创建并启动一个线程来执行新提交的任务。
  5. 如果workerCount >= maximumPoolSize,并且线程池内的阻塞队列已满, 则根据拒绝策略来处理该任务,默认的处理方式是直接抛异常。

在查看ThreadPoolExecutor 源码时,我们需要了解一些基础知识,以便我们更好的理解源码。

工作队列

工作队列模块是线程池能够管理任务的核心部分。线程池的本质是对任务和线程的管理,而做到这一点最关键的思想就是将任务和线程两者解耦,不让两者直接关联,才可以做后续的分配工作。线程池中是以生产者消费者模式,通过一个阻塞队列来实现的。阻塞队列缓存任务,工作线程从阻塞队列中获取任务。

阻塞队列(BlockingQueue) 是一个支持两个附加操作的队列。这两个附加的操作是:在队列为空时,获取元素的线程会等待队列变为非空。当队列满时,存储元素的线程会等待队列可用。阻塞队列常用于生产者和消费者的场景,生产者是往队列里添加元素的线程,消费者是从队列里拿元素的线程。阻塞队列就是生产者存放元素的容器,而消费者也只从容器里拿元素。

  • ArrayBlockingQueue:列表形式的工作队列,必须要有初始队列大小,有界队列,先进先出。
  • LinkedBlockingQueue:链表形式的工作队列,可以选择设置初始队列大小,有界/无界队列,先进先出,此队列的的长度为 Integer.MAX_VALUE 默认创建该队列由容量风险。
  • SynchronousQueue:SynchronousQueue不是一个真正的队列,而是一种在线程之间移交的机制。要将一个元素放入SynchronousQueue中,
    必须有另一个线程正在等待接受这个元素. 如果没有线程等待,并且线程池的当前大小小于最大值,那么ThreadPoolExecutor将创建
    一个线程, 否则根据饱和策略,这个任务将被拒绝。使用直接移交将更高效,因为任务会直接移交 给执行它的线程,而不是被首先放在队列中,
    然后由工作者线程从队列中提取任务. 只有当线程池是无界的或者可以拒绝任务时,SynchronousQueue才有实际价值。
  • PriorityBlockingQueue:优先级队列,无界队列,根据优先级来安排任务,任务的优先级是通过自然顺序或Comparator(如果任务实现了Comparator)来定义的。
  • DelayedWorkQueue:延迟的工作队列,无界队列。

拒绝策略

当有界队列被填满后,饱和策略开始发挥作用。ThreadPoolExecutor的饱和策略可以通过调用setRejectedExecutionHandler来修改。(如果某个任务被提交到一个已被关闭的Executor时,也会用到饱和策略)。饱和策略有以下四种,一般使用默认的AbortPolicy。

  • AbortPolicy:中止策略。默认的饱和策略,抛出未检查的RejectedExecutionException。调用者可以捕获这个异常,然后根据需求编写自己的处理代码。
  • DiscardPolicy:抛弃策略。当新提交的任务无法保存到队列中等待执行时,该策略会悄悄抛弃该任务。
  • DiscardOldestPolicy:抛弃最旧的策略。当新提交的任务无法保存到队列中等待执行时,则会抛弃下一个将被执行的任务,然后尝试重新提交新的任务。(如果工作队列是一个优先队列,那么“抛弃最旧的”策略将导致抛弃优先级最高的任务,因此最好不要将“抛弃最旧的”策略和优先级队列放在一起使用)。
  • CallerRunsPolicy:调用者运行策略。该策略实现了一种调节机制,该策略既不会抛弃任务,也不会抛出异常,而是将某些任务回退到调用者(调用线程池执行任务的主线程),从而降低新任务的流程。它不会在线程池的某个线程中执行新提交的任务,而是在一个调用了execute的线程中执行该任务。当线程池的所有线程都被占用,并且工作队列被填满后,下一个任务会在调用execute时在主线程中执行(调用线程池执行任务的主线程)。由于执行任务需要一定时间,因此主线程至少在一段时间内不能提交任务,从而使得工作者线程有时间来处理完正在执行的任务。在这期间,主线程不会调用accept,因此到达的请求将被保存在TCP层的队列中。如果持续过载,那么TCP层将最终发现它的请求队列被填满,因此同样会开始抛弃请求。当服务器过载后,这种过载情况会逐渐向外蔓延开来——从线程池到工作队列到应用程序再到TCP层,最终达到客户端,导致服务器在高负载下实现一种平缓的性能降低。

运行状态

RUNNING : 能接受新提交的任务,并且也能处理阻塞队列中的任务。
SHUTDOWN : 关闭状态,不再接受新提交的任务,但却可以继续处理阻塞队列中已保存的任务。
STOP: 不能接受新任务,也不处理队列中的任务,会中断正在处理任务的线程。
TIDYING : 所有的任务都已经中止了,workerCount(有效线程数) 为0 。
TERMINATED : 在terminated() 方法执行完后进入该状态。

在这里插入图片描述

ThreadPoolExecutor UML 图

在这里插入图片描述

源码

关键属性

//这个属性是用来存放 当前运行的worker数量以及线程池状态的
//int是32位的,这里把int的高3位拿来充当线程池状态的标志位,后29位拿来充当当前运行worker的数量
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
// 高3位用来表示运行状态,此值用于运行状态向左移动的位数,即29位
private static final int COUNT_BITS = Integer.SIZE - 3;     
// 线程数容量,低29位表示有效的线程数, 0001 1111 1111 1111 1111 1111 1111 1111
private static final int CAPACITY   = (1 << COUNT_BITS) - 1;
//存放任务的阻塞队列
private final BlockingQueue<Runnable> workQueue;
//worker的集合,用set来存放
private final HashSet<Worker> workers = new HashSet<Worker>();
//历史达到的worker数最大值
private int largestPoolSize;
//当队列满了并且worker的数量达到maxSize的时候,执行具体的拒绝策略
private volatile RejectedExecutionHandler handler;
/**
 * 线程超时时间,当线程数超过corePoolSize时生效, 
 * 如果有线程空闲时间超过keepAliveTime, 则会被终止
 */
private volatile long keepAliveTime;
//核心线程 worker的数量
private volatile int corePoolSize;
//最大worker的数量,一般当workQueue满了才会用到这个参数
private volatile int maximumPoolSize;
// 是否允许核心线程超时,默认false,false情况下核心线程会一直存活。
private volatile boolean allowCoreThreadTimeOut;

线程池运行状态

// 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 << COUNT_BITS,即高3位为111,该状态的线程池会接收新任务,并处理阻塞队列中的任务;
  • SHUTDOWN: 0 << COUNT_BITS,即高3位为000,该状态的线程池不会接收新任务,但会处理阻塞队列中的任务;
  • STOP : 1 <<COUNT_BITS,即高3位为001,该状态的线程不会接收新任务,也不会处理阻塞队列中的任务,而且会中断正在运行的任务;
  • TIDYING : 2 << COUNT_BITS,即高3位为010, 所有的任务都已经终止;
  • TERMINATED: 3 << COUNT_BITS,即高3位为011, terminated()方法已经执行完成

execute 方法

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();
       /**
       **1.workerCountOf获取线程池的当前线程数;
       **2.小于corePoolSize,执行addWorker()创建新线程(核心线程)执行command任务
       */
        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);  //调用拒绝策略
                //再次检查时,如果有效的线程数为0, 
            else if (workerCountOf(recheck) == 0) 
                addWorker(null, false); // 则新建一个线程(非核心线程)
        }
        //走到这里说明阻塞队列已经满了
        //尝试新建一个线程来处理任务(非核心)
        else if (!addWorker(command, false))
            reject(command); //创建失败 执行拒绝策略
    }

该方法对应上面线程池工作流流程。

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.
         //1).rs为RUNNING,通过校验。
         // 2).rs为STOP或TIDYING或TERMINATED,返回false。(STOP、TIDYING、TERMINATED:已经停止进入最后清理终止,不接受任务不处理队列任务)
         // 3).rs为SHUTDOWN,提交的任务不为空,返回false。(SHUTDOWN:不接受任务但是处理队列任务,因此任务不为空返回false)
         // 4).rs为SHUTDOWN,提交的任务为空,并且工作队列为空,返回false。 (状态为SHUTDOWN、提交的任务为空、工作队列为空,则线程池有资格关闭,直接返回false)
         //5).rs为SHUTDOWN,提交的任务为空,并且工作队列不为空,通过校验。(因为SHUTDOWN状态下刚好可以处理队列任务)

            if (rs >= SHUTDOWN &&
                ! (rs == SHUTDOWN &&
                   firstTask == null &&
                   ! workQueue.isEmpty()))
                return false;

            for (;;) {
                int wc = workerCountOf(c); //获取当前线程池数量
                if (wc >= CAPACITY ||
                    wc >= (core ? corePoolSize : maximumPoolSize))  // 线程池数量超过阈值 这返回false
                    return false;
                if (compareAndIncrementWorkerCount(c))  // 使用CAS 对当前线程数+1
                    break retry;
                c = ctl.get();  // Re-read ctl
                if (runStateOf(c) != rs) //走到这里说明CAS 增加线程数失败 判断当前线程状态是否发生变化
                    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()); //再次获取线程池状态
                    //1.当前线程池状态为RUNNING 校验通过
                    //2.当前线程池状态为SHUTDOWN 且当前提交任务为空 校验通过
                    if (rs < SHUTDOWN ||
                        (rs == SHUTDOWN && firstTask == null)) {
                        if (t.isAlive()) // precheck that t is startable //预先校验线程是否可以启动
                            throw new IllegalThreadStateException();
                        workers.add(w);  //加入worker 集合中
                        int s = workers.size();
                        if (s > largestPoolSize)
                            largestPoolSize = s;
                        workerAdded = true;
                    }
                } finally {
                    mainLock.unlock();
                }
                if (workerAdded) { //work 创建成功 启动线程
                    t.start();
                    workerStarted = true;
                }
            }
        } finally {
            if (! workerStarted) //启动线程 失败
                addWorkerFailed(w);//移除添加的worker  当前线程数减1,因为之前校验通过是加1过.
        }
        return workerStarted;
    }

addWork方法主要通过 for循环校验当前线程状态,使用CAS 增加当前线程数,提交的 firstTask 创建worker,线程启动时,会调用Worker里的run方法,执行runWorker(this)方法下面会介绍这个方法。

addWorkerFailed 方法

private void addWorkerFailed(Worker w) {
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            if (w != null)
                workers.remove(w); //移除worker
            decrementWorkerCount();//当前线程数减 1
            tryTerminate();//尝试中止线程
        } finally {
            mainLock.unlock();
        }
    }

该方法很简单,就是移除入参中的Worker并将workerCount-1,最后调用tryTerminate尝试终止线程池,tryTerminate见下文对应方法源码解读

runWorker 方法

public void run() {
   runWorker(this);
}
final void runWorker(Worker w) {
        Thread wt = Thread.currentThread();//获取当前线程
        Runnable task = w.firstTask;//获取worker 中的任务
        w.firstTask = null;
        w.unlock(); // allow interrupts
        boolean completedAbruptly = true;
        try {
            //当前任务不为空 或者 getTask 队列获取任务不为空 进入循环
            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
                //注释翻译如下:
                  //如果池正在停止,请确保线程被中断;如果没有,请确保线程不被中断。这第二种情况需要重新检查才能处理shutdown清除中断时无竞争
                if ((runStateAtLeast(ctl.get(), STOP) ||
                     (Thread.interrupted() &&
                      runStateAtLeast(ctl.get(), STOP))) &&
                    !wt.isInterrupted())
                    wt.interrupt(); //程池的状态为停止并且wt不为中断, 则将wt中断
                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++; //完成任务数加1
                    w.unlock();
                }
            }
            completedAbruptly = false;
        } finally {
            processWorkerExit(w, completedAbruptly);
        }
    }
    

该方法为Worker线程开始执行任务,首先执行当初创建Worker时的初始任务,接着从工作队列中获取任务执行。主要涉及两个方法:获取任务的方法getTask(见下文getTask源码解读)和执行Worker退出的方法processWorkerExit(见下文processWorkerExit源码解读)。注:processWorkerExit在处理正常Worker退出时,没有对workerCount-1,而是在getTask方法中进行workerCount-1。

getTask 方法

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.
           //1.如果线程池状态不是RUNNING 
           //2.当前线程为SHUTDOWN 且 队列为空 则需要移除当前worker
           if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
               decrementWorkerCount(); //移除当前worker 将workerCount -1 
               return null;
           }

           int wc = workerCountOf(c); //获取当前线程数量

           // Are workers subject to culling?
           //如果allowCoreThreadTimeOut为true,或者workerCount大于核心线程数
           boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
          // 如果wc超过最大线程数 或者 当前线程会超时并且已经超时,
           // 并且wc > 1 或者 工作队列为空,则返回null,代表当前Worker需要移除
           if ((wc > maximumPoolSize || (timed && timedOut))
               && (wc > 1 || workQueue.isEmpty())) {
               if (compareAndDecrementWorkerCount(c))
               // 返回null前将workerCount - 1,
               // 因此processWorkerExit中completedAbruptly=false时无需再减
                   return null;
               continue;
           }

           try {
          
           //根据上面的判断 wc > corePoolSize 当前线程数大于核心线程数,会调用poll 如果小于核心线程数,会调用take() 进行阻塞 直到获取到任务 这里也使得是核心线程数保活的关键
               Runnable r = timed ?
                   workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                   workQueue.take();
               if (r != null)
                   return r;
               timedOut = true;
           } catch (InterruptedException retry) {
               timedOut = false;
           }
       }
   }

Worker从工作队列获取任务,如果allowCoreThreadTimeOut为false并且 workerCount<=corePoolSize,则这些核心线程永远存活,并且一直在尝试获取工作队列的任务;否则workerCount>corePoolSize或者allowCoreThreadTimeOut=true ,当在keepAliveTime时间内获取不到任务,该线程的Worker会被移除。
Worker移除的过程:getTask方法返回null,导致runWorker方法中跳出while循环,调用processWorkerExit方法将Worker移除。注意:在返回null的之前,已经将workerCount-1,因此在processWorkerExit中,completedAbruptly=false的情况(即正常超时退出)不需要再将workerCount-1。

processWorkerExit 方法

private void processWorkerExit(Worker w, boolean completedAbruptly) {
        //1.如果worker是异常死亡,则completedAbruptly =true  会将workerCount-1
        //2. completedAbruptly = false 则表示getTask() 返回null,在getTask() 中已经减过 1
        if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
            decrementWorkerCount();

        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();//加锁
        try {
            completedTaskCount += w.completedTasks;//统计完成任务数 
            workers.remove(w);//移除worker 
        } finally {
            mainLock.unlock();
        }

        tryTerminate();//尝试终止线程池

        int c = ctl.get();
        if (runStateLessThan(c, STOP)) {//如果当前线程池没有停止(线程池状态为RUNNING ,SHUNTDOWN)
            if (!completedAbruptly) {//worker 不是异常死亡
               // min为线程池的理论最小线程数:如果允许核心线程超时则min为0,否则min为核心线程数
                int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
                //如果最小线程数为0 且队列不为空 将最小线程数设置为1
                if (min == 0 && ! workQueue.isEmpty())
                    min = 1;
                    // 如果当前线程数大于最小线程数,直接return
                if (workerCountOf(c) >= min)
                    return; // replacement not needed
            }
            / 如果代码走到这边,代表workerCountOf(c) < min,此时会走到下面的addWorker方法。
            // 通过getTask方法我们知道,当allowCoreThreadTimeOut为false
            // 并且workerCount<=corePoolSize时,是不会走到processWorkerExit方法的。
            // 因此走到这边只可能是当前移除的Worker是最后一个Worker,但是此时工作
            // 队列还不为空,因此min被设置成了1,所以需要在添加一个Worker来处理工作队列。
            addWorker(null, false);
        }
    }

tryTerminate 方法

final void tryTerminate() { // 尝试终止线程池
    for (;;) {
        int c = ctl.get();
        // 只有当前状态为STOP 或者 SHUTDOWN并且队列为空,才会尝试整理并终止
        // 1: 当前状态为RUNNING,则不尝试终止,直接返回
        // 2: 当前状态为TIDYING或TERMINATED,代表有其他线程正在执行终止,直接返回
        // 3: 当前状态为SHUTDOWN 并且 workQueue不为空,则不尝试终止,直接返回
        if (isRunning(c) || // 1
            runStateAtLeast(c, TIDYING) ||  // 2
            (runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))   // 3
            return;
        // 走到这代表线程池可以终止(通过上面的校验)
        // 如果此时有效线程数不为0, 将中断一个空闲的Worker,以确保关闭信号传播
        if (workerCountOf(c) != 0) { // Eligible to terminate 
            interruptIdleWorkers(ONLY_ONE);
            return;
        }
 
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();    // 加锁,终止线程池
        try {
            // 使用CAS将ctl的运行状态设置为TIDYING,有效线程数设置为0
            if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {  
                try {
                    terminated();   // 供用户重写的terminated方法,默认为空
                } finally {
                    // 将ctl的运行状态设置为TERMINATED,有效线程数设置为0
                    ctl.set(ctlOf(TERMINATED, 0));  
                    termination.signalAll();
                }
                return;
            }
        } finally {
            mainLock.unlock();
        }
        // else retry on failed CAS
    }
}

线程池如何调优

  1. 选择合适的线程池大小,过小的线程池可能会导致任务一直在排队, 过大的线程池可能会导致大家都在竞争 CPU 资源,增加上下文切换的开销 可以根据业务是 IO密集型还是 CPU 密集型来选择线程池大小:
    CPU 密集型:指的是任务主要使用来进行大量的计算,没有什么导致线程阻塞。一般这种场景的线程数设置为 CPU 核心数+1。
    IO 密集型:当执行任务需要大量的 io,比如磁盘 io,网络 io,可能会存在大量的阻塞,所以在 IO 密集型任务中使用多线程可以大大地加速任务的处理。一般线程数设置为 2*CPU 核心数。
  2. 任务队列的选择 :使用有界队列可以避免资源耗尽的风险,但是可能会导致任务被拒绝。 使用无界队列虽然可以避免任务被拒绝,但是可能会导致内存耗尽。一般需要设置有界队列的大小,比如 LinkedBlockingQueue。在构造的时候可以传入参数来限制队列中任务数据的大小,这样就不会因为无限往队列中扔任务导致系统的 oom。
  3. 尽量使用自定义的线程池,而不是使用 Executors 创建的线程池,因为 newFixedThreadPool 线程池由于使用了LinkedBlockingQueue,队列的容量默认无限大,实际使用中出现任务过多时会导致内存溢出;newCachedThreadPool 线程池由于核心线程数无限大,当任务过多的时候会导致创建大量的线程,可能机器负载过高导致服务宕机。
    优化方案参考
  • 13
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值