阿里面试必问之线程池(上)

前言

由于线程池涉及的知识点比较多,会以上中下三篇文章来叙述;
当我们面试时,如果遇到了面试官让你说一下线程池,我们可以先讲一下线程池的核心思想:
1.复用线程,降低线程创建与销毁代价。
2.提升处理速度,避免了等待线程创建的时间。
3.池化,方便统一管理与监控。

总体实现

ThreadPoolExecutor是线程池的核心类,主要负责线程管理和任务分发


线程池1.png

线程池本身就是一个生产-消费者模型,将任务生产与任务消费完全解耦,达到缓冲目的。

任务管理

当用户提交一个任务后,接下来这个任务将如何执行:
1.直接拿到核心线程进行任务执行
2.进入到缓冲队列,等待非核心线程
3.拒绝
源码如下:

   public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        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);
    }

源码逻辑:
1.如果workerCount < corePoolSize 则启动新线程
2.如果workerCount >= corePoolSize 则加入缓冲队列,等待执行
3.如果 workerCount < maximumPoolSize ,并且缓冲队列已满,则启动新线程
4.workerCount > maximumPoolSize 执行拒绝策略

任务缓冲

线程池本身是个生产消费者模式,用户将任务生产到任务队列,然后线程从任务队列拿任务。线程池的队列是BlockingQueue,但是线程池可以通过不同的Queue实现不一样的存储策略。
ArrayBlockingQueue:规定大小的阻塞队列,元素先进先出,要指定容量。
LinkedBlockingQueue:默认大小为Integer.MAX_VALUE,要注意容量。
PriorityBlockingQueue:自定义实现compareTo()方法来置顶元素排序规则,不能保证同优先级元素的顺序。
SynchronousQueue:每一个put操作必须等待take操作,否则不能添加元素,如果有空闲线程则会重复使用,线程空闲了60s后会被自动回收。

任务拒绝

线程池有一个最大的容量,当线程池的任务缓存队列已满,并且线程池中的线程数目达到maximumPoolSize时,就需要拒绝掉该任务,采取任务拒绝策略,保护线程池。

下篇会讲一下线程池中的worker是如何执行任务的,线程是如何复用的。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。