Java线程池

背景介绍

Java中的线程池是运用场景最多的并发框架,几乎所有需要异步或并发执行任务的程序都可以使用线程池。开发中,合理使用线程池会带来3个好处:

  1. 降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
  2. 提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
  3. 提高线程的可管理性。线程是稀缺资源,如果无限制地创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一分配、调优和监控。

线程池工具类

jdk中Executors工具类提供了一些常用的线程池。

  • newFixedThreadPool:返回一个核心线程数为 nThreads 的线程池
public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, 								  new LinkedBlockingQueue<Runnable>());
}
  • newSingleThreadExecutor:返回一个核心线程数为 1 的线程池
    public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }
  • newCachedThreadPool:按需分配线程的线程池
    public static ExecutorService newCachedThreadPool() {
        // 核心线程数为0,工作队列不存任务,所有线程全都是非核心线程
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

都是调用ThreadPoolExecutor构造方法来进行线程池的创建。

阿里巴巴Java开发手册中明确指出,『不允许』使用Executors创建线程池,生产中尽量使用 ThreadPoolExecutor 的构造方法自定义去创建线程池。如下:

public class ThreadPoolTest {
    public static void main(String[] args) {
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
                2,      // 核心线程数
                5,     // 最大线程数
                200,   // 非核心工作线程在阻塞队列位置等待的时间
                TimeUnit.SECONDS,  // 非核心工作线程在阻塞队列位置等待的单位
                new LinkedBlockingQueue<>(), // 阻塞队列,存放任务的地方
                new ThreadPoolExecutor.AbortPolicy() // 拒绝策略:这里有四种
        );

        for (int i = 0; i < 10; i++) {
            MyTask task = new MyTask();
            executor.execute(task);
        }

        // 关闭线程
        executor.shutdown();

    }
}

// 待执行任务
class MyTask implements Runnable {
    @Override
    public void run() {
        System.out.println("我被执行了....");
    }
}

线程池实现原理

当有新任务到线程池时,线程池执行流程:

Java线程池_第1张图片

Java线程池_第2张图片

提交任务,ThreadPoolExecutor执行execute方法分下面4种情况:

  1. 如果当前运行的线程少于corePoolSize,则创建新线程来执行任务(注意,执行这一步骤需要获取全局锁)。
  2. 如果运行的线程等于或多于corePoolSize,则将任务加入BlockingQueue。
  3. 如果无法将任务加入BlockingQueue(队列已满),则创建新的线程来处理任务(注意,执行这一步骤需要获取全局锁)。
  4. 如果创建新线程将使当前运行的线程超出maximumPoolSize,任务将被拒绝,并调用RejectedExecutionHandler.rejectedExecution()方法(默认拒绝策略)。

ThreadPoolExecutor采取上述步骤的总体设计思路,是为了在执行execute()方法时,尽可能地避免获取全局锁(那将会是一个严重的可伸缩瓶颈)。在ThreadPoolExecutor完成预热之后(当前运行的线程数大于等于corePoolSize),几乎所有的execute()方法调用都是执行步骤2,而步骤2不需要获取全局锁。

源码

1.全参构造器:(七大参数

public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler) {
    this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
         Executors.defaultThreadFactory(), handler);
}

public ThreadPoolExecutor(int corePoolSize, // 1.核心线程数
                              int maximumPoolSize, // 2.最大线程数
                              long keepAliveTime, // 3.非核心线程空闲时终止前等待新任务的最长时间
                              TimeUnit unit, // 4.keepAliveTime参数的时间单位
                              BlockingQueue<Runnable> workQueue, // 5.任务队列(阻塞队列),在执行任务之前用于保存任务的队列
                              ThreadFactory threadFactory, // 6.线程工厂,用来创建新的线程
                              RejectedExecutionHandler handler) // 7.线程池满时,执行的拒绝策略
{
    	// 参数校验
        if (corePoolSize < 0 || maximumPoolSize <= 0 || maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
    	// 赋值成员变量
        this.acc = System.getSecurityManager() == null ?
                null :
                AccessController.getContext();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

2.四大拒绝策略

Java线程池_第3张图片

  • AbortPolicy(默认拒绝策略):直接抛出异常
    public static class AbortPolicy implements RejectedExecutionHandler {
        
        public AbortPolicy() { }

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

Java线程池_第4张图片

  • CallerRunsPolicy:线程池满时,将任务交给调用者处理
    public static class CallerRunsPolicy implements RejectedExecutionHandler {

        public CallerRunsPolicy() { }

        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            if (!e.isShutdown()) {
                r.run();
            }
        }
    }
  • DiscardOldestPolicy:当线程池满了,将队列中最久未处理的任务弹出,执行当前任务
    public static class DiscardOldestPolicy implements RejectedExecutionHandler {
        public DiscardOldestPolicy() { }

        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            if (!e.isShutdown()) {
                // 将队列中最久未处理的任务弹出
                e.getQueue().poll();
                // 执行当前任务
                e.execute(r);
            }
        }
    }
  • DiscardPolicy: 不做任何处理,静默方式丢弃被拒绝的任务
    public static class DiscardPolicy implements RejectedExecutionHandler {
       
        public DiscardPolicy() { }

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

可以自己实现RejectedExecutionHandler接口,自定义任务拒绝策略,可以将拒绝的任务放至数据库等存储,等后续在执行。

3.其余成员变量

// 该数值代表两个意思:【按位拆分】
// 高3位表示当前线程池的状态
// 低29位表示当前线程池工作线程的个数
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));

//  COUNT_BITS = 29
private static final int COUNT_BITS = Integer.SIZE - 3;
// CAPACITY就是当前工作线程能记录的工作线程的最大个数
private static final int CAPACITY   = (1 << COUNT_BITS) - 1;

// 111:代表RUNNING状态,RUNNING可以处理任务,并且处理阻塞队列中的任务。
private static final int RUNNING    = -1 << COUNT_BITS;
// 000:代表SHUTDOWN状态,不会接收新任务,正在处理的任务正常进行,阻塞队列的任务也会做完。
private static final int SHUTDOWN   =  0 << COUNT_BITS;
// 001:代表STOP状态,不会接收新任务,正在处理任务的线程会被中断,阻塞队列的任务一个不管。
private static final int STOP       =  1 << COUNT_BITS;
// 010:代表TIDYING状态,这个状态是否SHUTDOWN或者STOP转换过来的,代表当前线程池马上关闭,就是过渡状态。
private static final int TIDYING    =  2 << COUNT_BITS;
// 011:代表TERMINATED状态,这个状态是TIDYING状态转换过来的,转换过来只需要执行一个terminated方法。
private static final int TERMINATED =  3 << COUNT_BITS;

// 基于&运算的特点,保证只会拿到ctl高三位的值 【线程池状态】
private static int runStateOf(int c)     { return c & ~CAPACITY; }
// 基于&运算的特点,保证只会拿到ctl低29位的值 【线程池中工作线程状态】
private static int workerCountOf(int c)  { return c & CAPACITY; }

线程池的状态变化流程图:

Java线程池_第5张图片

4.execute()和submit()向线程池提交任务☆

  • Step1:当前的线程池个数小于核心线程数,直接添加核心线程即可
  • Step2:当前的线程池个数大于等于核心线程数,将任务添加至阻塞队列中
  • Step3:如果添加阻塞队列失败,则需要添加非核心线程数处理任务
  • Step4:如果添加非核心线程数失败(线程池满了),执行拒绝策略
public void execute(Runnable command) {
    // 如果当前传过来的任务是null,直接抛出异常即可
    if (command == null)
        throw new NullPointerException();
    // 获取当前的数据值
    int c = ctl.get();
    
//==========================线程池第一阶段:启动核心线程数开始=================================
    // Step1:获取ctl低29位的数值(线程池当前工作线程数量),与初始化核心线程数相比
    if (workerCountOf(c) < corePoolSize) {
        // Step2:添加一个核心线程
        if (addWorker(command, true)){
            return;
        }
        // 更新一下当前值
        c = ctl.get();
    }
//==========================线程池第一阶段:启动核心线程数结束=================================
    
    // 如果走到下面会出现两种情况:
    // 1、核心线程数满了,需要往阻塞队列里面扔任务
    // 2、核心线程数满了,阻塞队列也满了,执行拒绝策略
    
//==========================线程池第二阶段:任务放至阻塞队列开始================================
    // 判断线程池状态是不是Running的状态(RUNNING可以处理任务,并且处理阻塞队列中的任务)
    // 如果是Running的状态,则可以将任务放至阻塞队列中
    // 这里如果放阻塞队列失败了,证明阻塞队列满了
    if (isRunning(c) && workQueue.offer(command)) {
        // 再次更新数值
        int recheck = ctl.get();
        // 再次校验当前的线程池状态是不是Running
        // 如果线程池状态不是Running的话,需要移除刚刚放的任务
        if (!isRunning(recheck) && remove(command)){
            // 执行拒绝策略
            reject(command);
        } 
        // 如果到这里,说明上面阻塞队列中已经有数据了
        // 如果线程池中线程个数为0的话,需要创建一个非核心工作线程去执行该任务
        // 不能让人家堵塞着
        else if (workerCountOf(recheck) == 0){
            addWorker(null, false);
        }
    }
//==========================线程池第二阶段:任务放至阻塞队列结束===========================

    // 如果走到这里的逻辑,证明上面的逻辑没走通,有以下两种情况:
    // 1、线程池的状态不是Running
    //    1.1 如果是这种情况,下面的添加非核心工作线程失败执行拒绝策略,但这个并不是这个逻辑的重点
    // 2、阻塞队列添加任务失败(阻塞队列满了)
    //    2.1 这种情况才是我们需要关心的
    //    2.2 阻塞队列满了,添加非核心工作线程
    //    2.3 若添加非核心工作线程失败,证明已经到达maximumPoolSize的限制,执行拒绝策略
//==========================线程池第三阶段:启动非核心线程数开始==============================
    // 添加一个非核心工作线程,线程池满了执行下面拒绝策略
    else if (!addWorker(command, false))
        // 工作队列中添加任务失败,执行拒绝策略 ====线程池第四阶段====
        reject(command);
//==========================线程池第三阶段:启动非核心线程数结束=============================
}

submit依旧是执行execute方法逻辑,只不过会将当前任务返回

    public Future<?> submit(Runnable task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<Void> ftask = newTaskFor(task, null);
        execute(ftask);
        return ftask;
    }

execute流程图:
Java线程池_第6张图片

5.addWorker()添加线程启动线程

5.1 校验

  • 校验当前线程池的状态
  • 校验当前线程池工作线程的个数(核心线程数、最大工作线程数)
    private boolean addWorker(Runnable firstTask, boolean core) {
        // 与下面break retry,结束整个循环,continue retry继续执行外层循环
        retry:
        for (;;) {
             // 获取当前线程池的数值(ctl)
            int c = ctl.get();
            // 获取数值高三位,线程状态
            int rs = runStateOf(c);

        //==========================线程池状态判断=================================
        // rs >= SHUTDOWN:代表当前线程池状态为:SHUTDOWN、STOP、TIDYING、TERMINATED,线程池状态异常
        // SHUTDOWN状态(不会接收新任务,正在处理的任务正常进行,阻塞队列的任务也会做完)
        // 如果当前的状态是SHUTDOWN状态并且阻塞队列任务不为空且新任务为空
        // 需要新起一个非核心工作线程去执行任务
        // 如果不是前面的,直接返回false即可
            if (rs >= SHUTDOWN && ! (rs == SHUTDOWN && firstTask == null &&
                   ! workQueue.isEmpty()))
                return false;

        //==========================工作线程个数判断===============================
            for (;;) {
                // 获取当前线程池中线程的个数
                int wc = workerCountOf(c);
          // core代表是否是核心线程 
          // 1、如果线程池线程的个数是否超过了工作线程的最大个数
          // 2、根据当前core判断创建的是大于核心线程数(corePoolSize)还是非核心线程数(maximumPoolSize)
                if (wc >= CAPACITY || wc >= (core ? corePoolSize : maximumPoolSize))
                    return false;
                // cas将线程池中工作线程+1
                if (compareAndIncrementWorkerCount(c))
                    // CAS成功后,直接退出外层循环,代表可以执行添加工作线程操作了。
                    break retry;
                // 获取当前线程池的数值(ctl)
                c = ctl.get();  
            // 再一次获取当前线程池的状态
            // 判断当前线程池的状态等不等于上面的rs
            // 如果程池的状态被人更改了,需要重新执行外层for循环判断逻辑
                if (runStateOf(c) != rs)
                    continue retry;
            }
        }

       // ...

5.2 添加线程

addWorker()源码后半部分

  • 添加线程、开启线程
	   {boolean workerStarted = false;
        boolean workerAdded = false;
        Worker w = null;
        try {
            // 将当前的任务封装成Worker
            w = new Worker(firstTask);
             // 拿到当前Worker的线程
            final Thread t = w.thread;
            if (t != null) {
                // 【加锁】,保证线程安全(workers、largestPoolSize)
                final ReentrantLock mainLock = this.mainLock;
                mainLock.lock();
                try {
                    // 获取线程池的状态
                    int rs = runStateOf(ctl.get());
                    
                // 1、rs < SHUTDOWN:保证当前线程池的状态一定是RUNNING状态
                // 2、rs == SHUTDOWN && firstTask == null:如果当前线程池是SHUTDOWN状态且新任务为空
                    if (rs < SHUTDOWN || (rs == SHUTDOWN && firstTask == null)) {
                        // 判断当前线程是否存活
                        if (t.isAlive()) 
                            throw new IllegalThreadStateException();
                        // private final HashSet workers = new HashSet();
                        // 添加到works中
                        workers.add(w);
                        int s = workers.size();
                    // largestPoolSize线程池中最大线程个数的记录
                    // 如果当前的大小比最大的还要大,替换即可
                        if (s > largestPoolSize)
                            largestPoolSize = s;
                        // worker添加成功
                        workerAdded = true;
                    }
                } finally {
                    // 解锁
                    mainLock.unlock();
                }
                // 线程添加成功,随即启动线程
                if (workerAdded) {
                    t.start();
                    // 线程启动成功
                    workerStarted = true;
                }
            }
        } finally {
            // 线程未启动成功,移除workers中当前worker
            if (!workerStarted)
                addWorkerFailed(w);
        }
        return workerStarted;
        }
		
		// Worker的初始化
        Worker(Runnable firstTask) {
            setState(-1); 
            this.firstTask = firstTask;
            // 线程工厂创建线程
            this.thread = getThreadFactory().newThread(this);
        }

	// 线程启动失败,移除当前worker
    private void addWorkerFailed(Worker w) {
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            if (w != null)
                workers.remove(w);
            decrementWorkerCount();
            tryTerminate();
        } finally {
            mainLock.unlock();
        }
    }

6.Worker 源码

// Worker继承了AQS,目的:工作线程加解锁
// Worker实现了Runnable,执行run方法中自定义内部方法
private final class Worker extends AbstractQueuedSynchronizer implements Runnable{
    
    private static final long serialVersionUID = 6138294804551838833L;
    // 当前线程工厂创建的线程(也是执行任务使用的线程)
    final Thread thread;
    
    // 当前的第一个任务
    Runnable firstTask;
  
    // 记录执行了多少个任务
    volatile long completedTasks;

    // 构造方法
    Worker(Runnable firstTask) {
        // 将State设置为-1,代表当前不允许中断线程
        setState(-1); 
        // 设置任务
        this.firstTask = firstTask;
        // 设置线程
        this.thread = getThreadFactory().newThread(this);
    }

    // 线程启动执行的方法
    public void run() {
        runWorker(this);
    }
    
    // =======================Worker管理中断================================   
    // 当前方法是中断工作线程时,执行的方法
    void interruptIfStarted() {
        Thread t;
        // 只有Worker中的state >= 0的时候,可以中断工作线程
        if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
            try {
                // 如果状态正常,并且线程未中断,中断线程
                t.interrupt();
            } catch (SecurityException ignore) {
            }
        }
    } 
}

7.runWorker源码

    public void run() {
        runWorker(this);
    }

	final void runWorker(Worker w) {
      	// 拿到当前的线程
        Thread wt = Thread.currentThread();
     	// 拿到当前Worker的第一个任务
        Runnable task = w.firstTask;
     	// 置空
        w.firstTask = null;
     	// 解锁
        w.unlock(); // allow interrupts
        boolean completedAbruptly = true;
        try {
             // 如果任务不等于空 或者 从阻塞队列中拿到的任务不等于空 如果队列中拿不到任务一直循环取任务
            while (task != null || (task = getTask()) != null) {
                // 加锁
                w.lock();
                // 如果线程池状态 >= STOP,确保线程中断
                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;
                    // 任务执行数+1
                    w.completedTasks++;
                    // 解锁
                    w.unlock();
                }
            }
            completedAbruptly = false;
        } finally {
            // 移除worker,累加线程完成任务数
            processWorkerExit(w, completedAbruptly);
        }
    }

8.getTask 源码

private Runnable getTask() {
    // 超时的标记
    boolean timedOut = false; 

    // 死循环拿数据
    for (;;) {
        // 拿到当前的ctl
        int c = ctl.get();
        // 获取其线程池状态
        int rs = runStateOf(c);

        // 如果线程池状态是STOP,没有必要处理阻塞队列任务,直接返回null
        // 如果线程池状态是SHUTDOWN,并且阻塞队列是空的,直接返回null
        if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
            // 线程池中的线程个数-1
            decrementWorkerCount();
            return null;
        }

        // 当前线程池中线程个数
        int wc = workerCountOf(c);

        // 这里是个重点
        // allowCoreThreadTimeOut:是否允许核心线程数超时(开启之后),核心线程数也会执行下面的超时逻辑
        // wc > corePoolSize:当前线程池中的线程个数大于核心线程数
        boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;

        // wc > maximumPoolSize:基本不存在
        // timed && timedOut:第一次肯定是失败的(timedOut = false)
        if ((wc > maximumPoolSize || (timed && timedOut))
            // 1、线程个数 > 1
            // 2、阻塞队列是空的
            && (wc > 1 || workQueue.isEmpty())) {
            // 线程池的线程个数-1
            if (compareAndDecrementWorkerCount(c)){
                return null;
            }
            continue;
        }

        try {
            // 根据前面的timed的值(当前线程池中的线程个数是否大于核心线程数)
            // 如果大于,执行workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS)带有时间的等待,超过时间无任务,会返回null
            // 如果小于等于,执行workQueue.take(),死等任务,不会返回null 【获取队列中任务】
            Runnable r = timed ? workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) : workQueue.take();
            // 任务不为空,返回队列中任务
            if (r != null){
                return r;
            }
            // 到这里,说明获取任务超时 timedOut置为true
            // 上面 if((wc > maximumPoolSize || (timed && timedOut)) (timed && timedOut) 为 true
            // wc > 1 || workQueue.isEmpty():当线程大于1或者阻塞队列无数据,直接返回null,减少线程个数
            timedOut = true;
        } catch (InterruptedException retry) {
            timedOut = false;
        }
    }
}

9.processWorkerExit源码

private void processWorkerExit(Worker w, boolean completedAbruptly) {
    // 正常退出: completedAbruptly=false
    // 出现异常退出: completedAbruptly=true
    if (completedAbruptly) 
        decrementWorkerCount();

    // 加锁
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        // 将当前worker的执行任务数累加到线程池中
        completedTaskCount += w.completedTasks;
        // 线程池移除该工作线程
        workers.remove(w);
    } finally {
        // 解锁
        mainLock.unlock();
    }

    tryTerminate();

    // 获取ctl的数据
    int c = ctl.get();
    // 只有SHUTDOWN、RUNNING会进入判断
    if (runStateLessThan(c, STOP)) {
        // 任务执行结束,正常退出
        if (!completedAbruptly) {
            // 是否允许核心线程超时 允许:0;不允许:核心线程数
            int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
            // 如果min=0并且阻塞队列不为空,将min设置成1
            if (min == 0 && ! workQueue.isEmpty())
                min = 1;
            // 当前线程数量大于等于最小值,直接返回即可
            if (workerCountOf(c) >= min){
                return;
            }
        }
        // 执行到这,说明线程池中没有线程了,并且还有阻塞任务
        // 只能添加一个非核心线程去处理这些任务
        addWorker(null, false);
    }
}

10.shutdown()和shutdownNow()关闭线程

shutdownNow 方法

  • 将线程池状态修改为Stop(线程池不再接受新的任务,不再处理已提交的任务,并且会中断正在执行的任务)
  • 将线程池中的线程全部中断
  • 移除线程池中任务队列中的所有任务
  • 将线程池的状态从:Stop --> TIDYING --> TERMINATED,正式标记线程池的结束(唤醒所有等待的主线程)
    public List<Runnable> shutdownNow() {
        List<Runnable> tasks;
        // 加锁
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            checkShutdownAccess();
            // 将线程池状态改为stop
            advanceRunState(STOP);
            // 将所有工作线程中断
            interruptWorkers();
            // 将工作队列中的任务全部移除(任务队列中未执行的任务)
            tasks = drainQueue();
        } finally {
            // 解锁
            mainLock.unlock();
        }
        // 查看当前线程池是否可以变为TERMINATED状态
        // 从 Stop 状态修改为 TIDYING,再修改为 TERMINATED
        tryTerminate();
        return tasks;
    }

// 将线程池的状态修改为Stop
private void advanceRunState(int targetState) {
    // 进来直接死循环
    for (;;) {
        // 获取当前的ctl
        int c = ctl.get();
        // runStateAtLeast(c, targetState):当前的c是不是大于STOP(如果大于Stop的话,说明线程池状态已经终止
        // 基于CAS,将ctl从c修改为Stop状态,不修改工作线程个数,仅仅将状态修改为Stop
        // 如果可以修改成功,直接退出即可
        if (runStateAtLeast(c, targetState) || ctl.compareAndSet(c, ctlOf(targetState, workerCountOf(c))))
            break;
    }
}

// 将线程池中的线程全部中断
private void interruptWorkers() {
    // 加锁
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        // 循环遍历线程组
        for (Worker w : workers)
            // 中断线程
            w.interruptIfStarted();
    } finally {
        // 解锁
        mainLock.unlock();
    }
}

// 删除工作队列中所有任务
private List<Runnable> drainQueue() {
    // 任务队列
    BlockingQueue<Runnable> q = workQueue;
    // 返回的结果
    ArrayList<Runnable> taskList = new ArrayList<Runnable>();
    q.drainTo(taskList);
    if (!q.isEmpty()) {
        // 将任务从workQueue移除并且放到taskList中
        for (Runnable r : q.toArray(new Runnable[0])) {
            if (q.remove(r))
                taskList.add(r);
        }
    }
    // 最终返回即可
    return taskList;
}

final void tryTerminate() {
    for (;;) {
        // 拿到ctl
        int c = ctl.get();
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            // CAS将当前的ctl设置成TIDYING
            if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {
                try {
                    // 【钩子函数】,在线程池销毁之前可以做一些自定义操作
                    terminated();
                } finally {
                    // 将ctl设置成TERMINATED标志着线程池的正式结束
                    ctl.set(ctlOf(TERMINATED, 0));
                    // 线程池提供了一个方法,主线程在提交任务到线程池后,是可以继续做其他操作的。
                    // 线程池终止后,要唤醒那些调用了awaitTermination方法的线程(等线程池一段时间,此时主线程可以做自己的事)
                    // 【当时等待线程池返回的主线程,虽然线程池已经销毁了,但他们也必须要唤醒,不能一直死等】
                    termination.signalAll();
                }
                return;
            }
        } finally {
            // 解锁
            mainLock.unlock();
        }
    }
}

shutdown 方法

    public void shutdown() {
        // 加锁
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            checkShutdownAccess();
            // 将线程池状态修改为SHUTDOWN
            advanceRunState(SHUTDOWN);
            // 将线程池中的线程全部中断
            interruptIdleWorkers();
            onShutdown(); // hook for ScheduledThreadPoolExecutor
        } finally {
            mainLock.unlock();
        }
         // 查看当前线程池是否可以变为TERMINATED状态
   		 // 从 SHUTDOWN 状态修改为 TIDYING,再修改为 TERMINATED
        tryTerminate();
    }

合理配置线程池

想要合理地配置线程池,就必须首先分析任务特性,有几个角度分析:

  • 任务的性质:CPU密集型任务、IO密集型任务和混合型任务。
  • 任务的优先级:高、中和低。
  • 任务的执行时间:长、中和短。
  • 任务的依赖性:是否依赖其他系统资源,如数据库连接。

针对不同性质任务,采用不同线程池配置:

  1. CPU密集型任务应配置尽可能小的线程,如配置Ncpu+1个线程的线程池。
  2. IO密集型任务线程并不是一直在执行任务,则应配置尽可能多的线程,如2*Ncpu。
  3. 混合型的任务,如果可以拆分,将其拆分成一个CPU密集型任务和一个IO密集型任务,只要这两个任务执行的时间相差不是太大,那么分解后执行的吞吐量将高于串行执行的吞吐量。如果这两个任务执行时间相差太大,则没必要进行分解。可以通过Runtime.getRuntime().availableProcessors()方法获得当前设备的CPU个数。
  4. 优先级不同的任务可以使用优先级队列PriorityBlockingQueue来处理。它可以让优先级高的任务先执行。
  5. 执行时间不同的任务可以交给不同规模的线程池来处理,或者可以使用优先级队列,让执行时间短的任务先执行。
  6. 依赖数据库连接池的任务,因为线程提交SQL后需要等待数据库返回结果,等待的时间越长,则CPU空闲时间就越长,那么线程数应该设置得越大,这样才能更好地利用CPU。【IO密集型】

建议使用有界队列。有界队列能增加系统的稳定性和预警能力,可以根据需要设大一点儿,比如几千。

线程池的监控

如果在系统中大量使用线程池,则有必要对线程池进行监控,方便在出现问题时,可以根据线程池的使用状况快速定位问题。

通过线程池提供的参数进行监控,在监控线程池的时候可以使用以下属性:

  • taskCount:线程池需要执行的任务数量。
  • completedTaskCount:线程池在运行过程中已完成的任务数量,小于或等于taskCount。
  • largestPoolSize:线程池里曾经创建过的最大线程数量。通过这个数据可以知道线程池是否曾经满过。如该数值等于线程池的最大大小,则表示线程池曾经满过。
  • getPoolSize:线程池的线程数量。如果线程池不销毁的话,线程池里的线程不会自动销毁,所以这个大小只增不减。
  • getActiveCount:获取活动的线程数。

通过扩展线程池进行监控。可以通过继承线程池来自定义线程池,重写线程池的beforeExecute、afterExecute和terminated方法,在任务执行前、执行后和线程池关闭前执行一些代码来进行监控。

可以监控任务的平均执行时间、最大执行时间和最小执行时间等。这几个方法在线程池里是空方法。

参考资料

  1. https://xiaohuang.blog.csdn.net/article/details/130537909
  2. 《Java并发编程的艺术》

你可能感兴趣的:(#,juc,Java基础,java,开发语言)