为了账号安全,请及时绑定邮箱和手机立即绑定

CachedThreadPool是怎样实现线程复用的?

CachedThreadPool是怎样实现线程复用的?

慕虎7371278 2019-02-25 11:01:20
按我的理解,假设我不断给线程池提交新的同样的Runnable,线程池会复用已经运行完但没销毁的线程,来减少创建线程的开销。 但是,如果我们多次调用线程的start()方法,程序会崩溃的,而且Thread类也没有类似SetRunnable之类的方法,那么请问线程池是怎样做到的呢? 另外,当需要重复开启同样的线程时,最佳实践是什么?谢谢!
查看完整描述

2 回答

?
UYOU

TA贡献1878条经验 获得超4个赞

我猜题主是使用 Executors.newCachedThreadPool();这个方法来创建线程池的吧,不知道你有没有点进去这个方法看看里面是怎么来创建线程池的。
我就来帮你看下源码。
创建线程池最终调用的是这个方法。

/**
     * 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 :核心线程数量,就算线程的状态是闲置也不会销毁
maximumPoolSize: 最大线程数量
keepAliveTime :超出核心线程的数量可以闲置多久,超过这个时间就销毁了
unit :keepAliveTime的单位
workQueue: 从泛型可以看出来里面装的都是Runnable,而且是一个阻塞的队列
ThreadFactory: 线程工厂
RejectedExecutionHandler: 任务拒绝策略,有四种可选

        1、直接丢弃(DiscardPolicy)
        2、丢弃队列中最老的任务(DiscardOldestPolicy)。
        3、抛异常(AbortPolicy)
        4、将任务分给调用线程来执行(CallerRunsPolicy)。

然后再看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();
        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);
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        else if (!addWorker(command, false))
            reject(command);
    }

但是,如果我们多次调用线程的start()方法,程序会崩溃的,而且Thread类也没有类似SetRunnable之类的方法,那么请问线程池是怎样做到的呢?

线程池是先找到一个闲置的线程,然后从workQueue这个阻塞队列中拿到你提交的任务(Runnable)然后去跑。(理想状态)

另外,当需要重复开启同样的线程时,最佳实践是什么?

当然是使用线程池咯,可以设置核心线程数量为1。

查看完整回答
反对 回复 2019-03-01
  • 2 回答
  • 0 关注
  • 430 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信