线程池ThreadPoolExecutor源码剖析

线程池ThreadPoolExecutor源码剖析

概览

  1. 线程的创建方式
  2. 线程池的参数
  3. 线程池执行流程图
  4. 执行流程源码解析
  5. 线程池状态图
  6. worker的封装
  7. 线程执行的后续处理
  8. 线程池中线程执行任务总体工作流程

一,线程的创建方式

  1. 继承Thread类

在这里插入图片描述

  1. 实现runnable接口

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UrdyouiC-1691579008075)(C:\Users\hejh\AppData\Roaming\Typora\typora-user-images\image-20230809160842904.png)]

  2. 实现callable接口,可以接收返回值

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-l497CyhI-1691579008077)(C:\Users\hejh\AppData\Roaming\Typora\typora-user-images\image-20230809161233187.png)]

  3. 自定义线程池或者使用JUC包下已经写好的线程池:线程池方式其实和上面没有区别,只不过上面需要频繁的创建和销毁线程,会造成一些不必要的额外资源消耗,所以在实际开发中肯定会采用线程 池的方式 Java中的Executors自带了一些创建线程池的方式

    Executors类下:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QLJADNcf-1691579008078)(C:\Users\hejh\AppData\Roaming\Typora\typora-user-images\image-20230809164946394.png)]

二,线程池的参数

java.util.concurrent.ThreadPoolExecutor是自定义线程池的类:

扫描二维码关注公众号,回复: 16748027 查看本文章
 /**
     * Creates a new {@code ThreadPoolExecutor} with the given initial
     * parameters.
     *
     * @param corePoolSize the number of threads to keep in the pool, even
     *        if they are idle, unless {@code allowCoreThreadTimeOut} is set
     * @param maximumPoolSize the maximum number of threads to allow in the
     *        pool
     * @param keepAliveTime when the number of threads is greater than
     *        the core, this is the maximum time that excess idle threads
     *        will wait for new tasks before terminating.
     * @param unit the time unit for the {@code keepAliveTime} argument
     * @param workQueue the queue to use for holding tasks before they are
     *        executed.  This queue will hold only the {@code Runnable}
     *        tasks submitted by the {@code execute} method.
     * @param threadFactory the factory to use when the executor
     *        creates a new thread
     * @param handler the handler to use when execution is blocked
     *        because the thread bounds and queue capacities are reached
     * @throws IllegalArgumentException if one of the following holds:<br>
     *         {@code corePoolSize < 0}<br>
     *         {@code keepAliveTime < 0}<br>
     *         {@code maximumPoolSize <= 0}<br>
     *         {@code maximumPoolSize < corePoolSize}
     * @throws NullPointerException if {@code workQueue}
     *         or {@code threadFactory} or {@code handler} is null
     */
    public ThreadPoolExecutor(int corePoolSize, //核心线程数
                              int maximumPoolSize, //最大线程数
                              long keepAliveTime, //空闲线程存活时间
                              TimeUnit unit, //时间单位
                              BlockingQueue<Runnable> workQueue, //阻塞队列
                              ThreadFactory threadFactory, //线程工厂
                              RejectedExecutionHandler handler) // 拒绝策略
  • corePoolSize 线程池核心线程大小 线程池中会维护一个最小的线程数量,即使这些线程处理空闲状态,他们也不会被销毁,除非设置了allowCoreThreadTimeOut。这里的最小线程数 量即是corePoolSize。

  • maximumPoolSize 线程池最大线程数量 一个任务被提交到线程池以后,首先会找有没有空闲存活线程,如果有则直接将任务交给这个空闲线程来执行,如果没有则会缓存到工作队列(后 面会介绍)中,如果工作队列满了,才会创建一个新线程,然后从工作队列的头部取出一个任务交由新线程来处理,而将刚提交的任务放入工作队 列尾部。线程池不会无限制的去创建新线程,它会有一个最大线程数量的限制,这个数量即由maximunPoolSize指定。

  • keepAliveTime 空闲线程存活时间。 一个线程如果处于空闲状态,并且当前的线程数量大于corePoolSize,那么在指定时间后,这个空闲线程会被销毁,这里的指定时间由keepAliveTime 来设定。

  • unit 空闲线程存活时间单位 keepAliveTime的计量单位

  • workQueue 工作队列 新任务被提交后,会先进入到此工作队列中,任务调度时再从队列中取出任务。jdk中提供了四种工作队列:

    1,ArrayBlockingQueue 基于数组的有界阻塞队列,按FIFO排序。新任务进来后,会放到该队列的队尾,有界的数组可以防止资源耗尽问题。当线程池中线程数量达到 corePoolSize后,再有新任务进来,则会将任务放入该队列的队尾,等待被调度。如果队列已经是满的,则创建一个新线程,如果线程数量已 经达到maxPoolSize,则会执行拒绝策略。

    2,LinkedBlockingQueue 基于链表的无界阻塞队列(其实最大容量为Interger.MAX),按照FIFO排序。由于该队列的近似无界性,当线程池中线程数量达到corePoolSize 后,再有新任务进来,会一直存入该队列,而不会去创建新线程直到maxPoolSize,因此使用该工作队列时,参数maxPoolSize其实是不起作用 的。

    3,SynchronousQueue 一个不缓存任务的阻塞队列,生产者放入一个任务必须等到消费者取出这个任务。也就是说新任务进来时,不会缓存,而是直接被调度执行该 任务,如果没有可用线程,则创建新线程,如果线程数量达到maxPoolSize,则执行拒绝策略。 4,PriorityBlockingQueue 具有优先级的无界阻塞队列,优先级通过参数Comparator实现。

  • threadFactory 线程工厂 创建一个新线程时使用的工厂,可以用来设定线程名、是否为daemon线程等等。

  • handler 拒绝策略 当工作队列中的任务已到达最大限制,并且线程池中的线程数量也达到最大限制,这时如果有新任务提交进来,该如何处理呢。这里的拒绝策略, 就是解决这个问题的,jdk中提供了4中拒绝策略:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rJeEfDbi-1691579008079)(C:\Users\hejh\AppData\Roaming\Typora\typora-user-images\image-20230809165906985.png)]

三,线程池执行流程图

四,执行流程源码解析

PS:以下方法都是在java.util.concurrent.ThreadPoolExecutor类下

1,属性

    //原子类,用于保存线程池的状态和工作线程的数量。总共32位,前3位表示线程池的状态,后29位表示工作线程的数量 
    private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
    private static final int COUNT_BITS = Integer.SIZE - 3;
    private static final int CAPACITY   = (1 << COUNT_BITS) - 1;

    // 线程池的状态,保存在前三位
    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;

    // ctl的构造和 拆分其得到线程池的状态和工作线程的数量
    private static int runStateOf(int c)     {
    
     return c & ~CAPACITY; }
    private static int workerCountOf(int c)  {
    
     return c & CAPACITY; }
    private static int ctlOf(int rs, int wc) {
    
     return rs | wc; }

2,execute方法

public void execute(Runnable command) {
    
    
        //健壮性判断
        if (command == null)
            throw new NullPointerException();
        int c = ctl.get();
        //如果工作线程的数量小于核心线程数,则创建线程处理任务,addWorker之前都要检查线程池状态  
        if (workerCountOf(c) < corePoolSize) {
    
    
            if (addWorker(command, true))
                return;
            //防止多线程情况下c变成其他值
            c = ctl.get();
        }
        //核心线程已满,如果线程池处于运行状态,将任务放进阻塞队列
        if (isRunning(c) && workQueue.offer(command)) {
    
    
            int recheck = ctl.get();
            //重复检查,如果不是运行状态,移除队列中的任务,拒绝任务。addWorker之前都要检查线程池状态
            if (! isRunning(recheck) && remove(command))
                reject(command);
            //如果工作线程为0,就添加一个线程,避免出现队列任务没有线程执行的情况。
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
       //添加线程处理这个任务,如果失败则拒绝。
        else if (!addWorker(command, false))
            reject(command);
    }

3,addWorker方法

private boolean addWorker(Runnable firstTask, boolean core) {
    
    
        retry:
        for (;;) {
    
    
            int c = ctl.get();
            int rs = runStateOf(c);

            // 仅在必要时检查队列是否为空。
            if (rs >= SHUTDOWN && // 线程池属于关闭状态,无法接受新任务,也无法处理池中任务,
                ! (rs == SHUTDOWN && // !SHUTDOWN,即是STOP,TIDYING,TERMINATED
                   firstTask == null && // firstTask不为空 -> 这里对应上述的addWorker(null,false)
                   ! workQueue.isEmpty()))// 工作队列为空
                return false;

            for (;;) {
    
    
                int wc = workerCountOf(c);
                //工作线程数再判断
                if (wc >= CAPACITY ||
                    wc >= (core ? corePoolSize : maximumPoolSize))
                    return false;
                //通过CAS操作增加工作线程数
                if (compareAndIncrementWorkerCount(c))
                    break retry; 
                c = ctl.get();  // Re-read ctl
                if (runStateOf(c) != rs)
                    continue retry;
                // CAS 由于workerCount 变化而失败;重试内循环
            }
        }
        //开始添加工作线程
        boolean workerStarted = false;
        boolean workerAdded = false;
        Worker w = null;
        try {
    
    
            w = new Worker(firstTask);
            final Thread t = w.thread;
            //判断t!=null的目的,防止t创建失败
            if (t != null) {
    
    
                final ReentrantLock mainLock = this.mainLock;
                mainLock.lock();//加锁的原因就是largestPoolSize,
                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)) {
    
    //处于shutdown状态,firstTask == null这里对应上述的addWorker(null,false)
                        if (t.isAlive()) // precheck that t is startable
                            throw new IllegalThreadStateException();
                        workers.add(w);//workers:线程池中所有工作线程的集合,
                        int s = workers.size();
                        // 如果任务线程大于记录的当前出现过的最大线程数,替换一下。
                        if (s > largestPoolSize)
                            largestPoolSize = s;
                        workerAdded = true;
                    }
                } finally {
    
    
                    mainLock.unlock();
                }
                //工作线程添加成功
                if (workerAdded) {
    
    
                    t.start();//开始执行
                    workerStarted = true;
                }
            }
        } finally {
    
    
            if (! workerStarted)
                //添加失败,处理方式
                addWorkerFailed(w);
        }
        return workerStarted;
    }

4,addWorkerFailed方法,添加线程失败方法

//通过上面的代码,总结哪些情况会出现添加失败的情况:
1,线程池不在运行状态
2,线程已经启动    
这2种情况都没有添加成功    workers.remove(w);不会有问题。
private void addWorkerFailed(Worker w) {
    
    
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();//因为decrementWorkerCount而加锁
        try {
    
    
            if (w != null)
                workers.remove(w);
            decrementWorkerCount();
            //重新检查线程池终止情况,以防此线程存在影响线程池终止。
            tryTerminate();
        } finally {
    
    
            mainLock.unlock();
        }
    }

五,线程池状态图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SnaGuPtK-1691579008080)(C:\Users\hejh\AppData\Roaming\Typora\typora-user-images\image-20230809180334809.png)]

六,worker的封装

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); // 添加标识,worker运行前,禁止中断(AQS)
            this.firstTask = firstTask;
            this.thread = getThreadFactory().newThread(this);
        }

        /** 具体运行调用外部(threadPoolExecutor)的方法  */
        public void run() {
    
    
            runWorker(this);
        }

        // Lock methods AQS的状态
        //
        // The value 0 represents the unlocked state.
        // The value 1 represents the locked state.

        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) {
    
    
                }
            }
        }
    }

java.util.concurrent.ThreadPoolExecutor#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 {
    
    
            //任务不为null,就一直循环,否则调用getTask尝试从阻塞队列获取任务
            while (task != null || (task = getTask()) != null) {
    
    
                w.lock();// 加锁的目的是表示当前任务正在执行,你shutdown任务也不会中断
                // 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();
                }
            }
            completedAbruptly = false;
        } finally {
    
    
            processWorkerExit(w, completedAbruptly);
        }
    }

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.
            if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
    
    
                decrementWorkerCount();
                return null;
            }

            int wc = workerCountOf(c);
            // Are workers subject to culling?
            boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
            //
            if ((wc > maximumPoolSize || (timed && timedOut))//当前工作线程数大于最大线程数 ,后面判断表示是否是允许核心线程超时并且真的超时
                && (wc > 1 || workQueue.isEmpty())) {
    
    //工作线程 > 1或者 阻塞队列为空
                if (compareAndDecrementWorkerCount(c))// 干掉当前工作线程并返回null,CAS的方式,如果失败,重新走一遍
                    return null;
                continue;
            }

            try {
    
    
                Runnable r = timed ?
                    // 这里是可能出现超时情况并且允许回收线程,那就阻塞这么久拿阻塞队列的任务
                    workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                //这里是线程阻塞在这,等待任务,不参与回收的情况,直到触发signal方法被唤醒,走catch继续下次循环
                    workQueue.take();
                if (r != null)
                    return r;
                timedOut = true;
            } catch (InterruptedException retry) {
    
    
                timedOut = false;
            }
        }
    }

七,线程执行的后续处理

processWorkerExit方法:

private void processWorkerExit(Worker w, boolean completedAbruptly) {
    
    
        if (completedAbruptly) // 如果停止,减一个工作线程数
            decrementWorkerCount();

        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();//加锁为了移除工作线程workers.remove(w);
        try {
    
    
            completedTaskCount += w.completedTasks;
            workers.remove(w);
        } finally {
    
    
            mainLock.unlock();
        }

        tryTerminate();// 尝试干掉线程池

        int c = ctl.get();
        if (runStateLessThan(c, STOP)) {
    
    
            // 如果不是认为停止,需要判断线程是否需要追加一个线程处理任务
            if (!completedAbruptly) {
    
    
                int min = allowCoreThreadTimeOut ? 0 : corePoolSize;// 查看核心线程是否允许超时
                if (min == 0 && ! workQueue.isEmpty())
                    min = 1;// 如果允许超时,并且工作队列不是空,就将min设置为1
                if (workerCountOf(c) >= min)// 如果工作线程数量大于核心线程数,就直接结束
                    return; // replacement not needed
            }
            addWorker(null, false);
        }
    }

八,线程池中线程执行任务总体工作流程

在这里插入图片描述

大致流程描述:

  1. 执行任务,核心线程数未达到corePoolSize该值,就创建线程。否则塞入任务队列。
  2. 任务队列满了,就创建线程,但是总线程数不能超过该值maximumPoolSize。
  3. 如果任务队列满了,线程数达到maximumPoolSize值,则执行失败策略。
  4. 工作线程则不停的轮询去队列中poll任务,如果poll为空,则工作线程执行结束(回收线程)。
  5. 如果工作线程数<=核心线程数corePoolSize,则使用take从队列中获取任务(核心线程一直await)。

首先getTask是在一个无限的for循环里面的, 判断 当前运行的线程数<=核心线程数 ,直接调用阻塞队列的take方法,无限阻塞下去,直到队列中有数据放入,唤醒退出for循环。

如果 当前运行的线程数> 核心线程数 ,如果队列没数据,会阻塞 keepAliveTime 时间才返回,继续for循环时,如果队列为空就退出for,销毁该阻塞在队列上的线程。

线程池流程总结

当任务提交时,如果当前正在运行的线程数 小于 核心线程数 时, 构造一个worker,并且调用worker的start开启一个线程。 线程的run方法为一个while循环。循环里面调用getTask从全局阻塞队列中取出数据,取出就立即执行,然后阻塞到getTask方法的队列上。

当任务提交时,如果 当前正在运行的线程数 大于等于 核心线程数 且 队列没有满,放入到全局阻塞队列里面,此时上面getTask 阻塞的线程将唤醒执行业务任务。

当任务提交时,如果 当前正在运行的线程数 大于等于 核心线程数 且 队列满了,会继续判断当前正在运行的线程数 的是否超过最大线程数,如果超过了,走拒绝任务策略,否则构造一个worker ,调用worker的start开启一个线程,执行第一步的while循环getTask的操作。

getTask操作

getTask时,如果当前运行线程数在核心线程数内,线程不紧张时,就会无限阻塞到队列上,直到队列有数据放入就返回执行,如果当前运行线程数大于了核心线程数,线程开的较多了。就会阻塞keepAliveTime时间,期间如果队列还没数据来,就会销毁这个worker的线程,让其回收。

KeepAliveTime的作用?

keepAliveTime(线程活动保持时间):线程池的工作线程空闲后,保持存活的时间。该参数只在线程数大于 corePoolSize时才有用, 超过这个时间的空闲线程将被终止;但是如果设置 allowCoreThreadTimeOut 为true,则核心线程数到了keepAliveTime时间没任务执行的话也会被销毁掉。 如果任务很多,并且每个任务执行的时间比较短,可以调大这个时间,提高线程的利用率。

猜你喜欢

转载自blog.csdn.net/Edward_hjh/article/details/132194867