线程池ThreadPoolExecutor简单总结

线程池ThreadPoolExecutor简单总结

线程池的构造函数,入参怎么理解?

线程池有几个概念,核心线程,核心线程数量,最大线程数,拒绝策略,任务队列。这些都是在初始化线程池的时候完成的(当然你也可以主动调用设置参数的方法)。

在说明这些参数的具体意义之前,先理解一个小场景,它和线程池的基本工作模式类似。

你有一家生产布偶玩具的小作坊,平时收到的订单量最多只需要你们一家四口就能完成,量少的时候甚至只需要一个人就能完成。但有时候订单量会骤增,你发现有些订单并不一定要最快速度完成,所以你会对这些订单做一个排队。当订单继续增加,就算有了排队机制,你仍然处理不过来。这时候你就想到了临时工,当然,招临时工这事儿你会外包给一个人力资源公司。在你的带领下,小作坊产品质量一直很好,客户不断的增加,订单越来越多,你得权衡这些订单能不能如期交付,毕竟不能如期交付的话,违约成本很高。所以,你就不得不拒绝一些订单,你还专门找了你的外甥来处理这些你拒绝的这些订单。
在看完上面这个场景之后,我们将其类比到线程池的参数:
corePoolSize 核心线程的数量。相当于你们家的一家四口,这个小作坊的核心成员。
maximumPoolSize 最大线程数量。相当于你的小作坊,最多能容纳多少人同时工作。
keepAliveTime 闲置线程存活的时间。相当于不干活的工人,在小作坊能待多久,等活儿的时间。
unit 闲置线程存活时间的单位。
workQueue 任务队列。相当于你对订单排序的队列,你可以按时间排序,或者其它规则。
threadFactory 线程工厂。相当于你外包给的那家人力公司,他为你提供工人。
handler 被拒绝的任务处理者。相当于你外甥,他会处理掉你拒绝的订单,有可能他再找人代工哦。

源码如下:

/**
     * 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,//keepAliveTime的单位
                              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;
    }

当任务提交到线程池,线程池会做什么?

先来看看execute的源码,干了什么。根据源码中的注释,执行了三个步骤:
1.如果正在执行的核心线程数量少于设定值(corePoolSize),则添加一个核心线程;

2.如果核心线程数量已经用完,则将当前任务添加到核心线程的队列中等待;(若添加成功,再检查一遍线程是否已不处于 RUNNING 状态,那么移除已经入队的这个任务,并且执行拒绝策略。因为第一次检查和添加的过程中可能线程池状态变化成非RUNNING状态)

3.如果将任务添加到队列失败,则添加一个非核心线程来执行该任务,若非核心线程添加也失败(比如添加的线程数超过最大限定的线程数量maximumPoolSize),则执行拒绝策略

代码:

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();
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
      // 如果到这里,要么当前线程数大于等于核心线程数,要么刚刚 addWorker 失败
      // 如果线程池处于 RUNNING 状态,把这个任务添加到任务队列 workQueue 中
        if (isRunning(c) && workQueue.offer(command)) {
            //添加成功后 再进行一次检查
            int recheck = ctl.get();
            //重新再检查线程是否处于running状态,如果没在running状态则移除任务
            if (! isRunning(recheck) && remove(command))
                reject(command);//没running,且从队列中移除,则调用拒绝策略
            else if (workerCountOf(recheck) == 0)//判断线程池中还有没有线程
                addWorker(null, false);
        }
        else if (!addWorker(command, false))//如果添加到队列失败,则开启新的非核心线程来执行
            reject(command);//如果开启非核心线程失败,则执行拒绝策略
    }

流程图:
通用线程池对任务的处理流程

线程什么时候被创建,什么时候被关闭?

1、核心线程创建时机:当一个任务被提交的线程池,而线程池中,没有空闲的线程可以用时创建(比如线程池已经有一个核心线程正在执行任务,核心线程数量设定为4,这时候来了一个新任务,则创建新线程,作为核心线程。)
2、核心线程关闭时机:一般情况下核心线程是不会被关闭的。除非allowCoreThreadTimeOut设置为true。
3、非核心线程的创建时机:当一个任务被提交到线程池,核心线程都已被占用,将该任务添加到队列也失败了,这时候就需要创建一个非核心线程来执行该任务。

4、非核心线程的关闭时机:当任务执行完成之后,且从任务队列里获取任务超时则关闭(这个超时时长就是非核心线程能闲置的时长getTask中的 如下代码);

                    Runnable r = timed ?
                    workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                    workQueue.take();

一个线程在线程池中的生命过程,这个线程会执行哪些关键方法?

1、addWorker的时候会调用线程工厂的newThread(Worker)方法,
2、创建的这个线程会调用Worker的run()方法
3、执行runWorker(Worker)方法
4、runWorker()方法中,不断的循环调用getTask()从workerQueue中获取任务
5.1、如果成功获取任务,则执行该任务的run()方法,执行完run继续步骤4的循环
5.2、获取任务超时,或者返回为空则表示任务队列已经没有多余任务,进入下一步
6、processWorkerExit(w, completedAbruptly)执行工作人员的撤退流程

通常说线程池复用线程是如何实现的?

当我们创建线程后,调用线程的run方法,用一个循环,不断的从任务队列中获取任务,然后执行这些任务的run()方法。详细见上一条问答。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值