ThreadPoolExecutor 线程池工作原理

为什么使用线程池

多线程场景下,线程的频繁创建和销毁,也就是频繁的进行上下文切换,消耗CPU资源,导致性能下降,为了解决这类问题,所以引入了线程池的概念,回收再利用已创建的线程资源

那么线程池具体是怎么工作的呢?

创建线程池

常规来讲,使用 Executors 能够快速的创建线程池,如下所示:

ExecutorService threadPool = Executors.newFixedThreadPool(1);
ExecutorService threadPool = Executors.newScheduledThreadPool(1);
ExecutorService threadPool = Executors.newSingleThreadExecutor();
ExecutorService threadPool = Executors.newCachedThreadPool();

但是结合阿里云的开发规范,不建议使用 Executors 的方式创建线程池,因为这样的处理方式无法让写代码的同学明确线程池的运行规则,从而规避资源耗尽的风险

Executors 方式的弊端

        a) newFixedThreadPool 和 newSingleThreadExecutor:

        主要问题是堆积的请求处理队列可能会耗费非常大的内存,甚至OOM。

        b) newCachedThreadPool 和 newScheduledThreadPool:

   主要问题是最大线程数是 Integer.MAX_VALUE,可能会创建数量非常多的线程,甚至OOM

针对上述情况,业界建议使用 ThreadPoolExecutor 的方式创建线程池:

内部封装了不同参数个数的构造方法,这里以最全的构造方法为例

ThreadPoolExecutor

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          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.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;
}

七个参数分别为:

        核心线程数、最大线程数、最大空闲时间、空闲时间单位、阻塞队列、线程工厂、拒绝策略

使用示例如下:

public class TestThreadPoolExecutor {

    private static ExecutorService threadPool = new ThreadPoolExecutor(2,
            5,
            10,
            TimeUnit.SECONDS,
            new ArrayBlockingQueue(1000),
            new ThreadFactory() {
                @Override
                public Thread newThread(Runnable r) {
                    return new Thread("测试线程");
                }
            },
            new ThreadPoolExecutor.DiscardPolicy()
    );

    public static void main(String[] args) {
        threadPool.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println("哈哈哈");
            }
        });
    }
}

工作原理

参数和使用方法知道了,那么 ThreadPoolExecutor 工作原理是怎样的呢?

线程池刚创建时,里面没有一个线程。

任务队列是作为参数传进来的,不过就算队列里有任务,线程池也不会立即执行它们,当调用 execute() 方法添加新任务时,线程池会做如下判断:

  • 如果正在运行的线程数量小于 corePoolSize,那么马上创建线程运行这个任务
  • 如果正在运行的线程数量大于或等于 corePoolSize,那么将这个任务放入队列
  • 如果这时候队列满了,而且正在运行的线程数量小于 maximumPoolSize,那么要创建非核心线程立刻运行这个任务
  • 如果队列满了,而且正在运行的线程数量大于或等于 maximumPoolSize,那么线程池会抛出异常 RejectExecutionException
  • 当一个线程完成任务时,它会从队列中取下一个任务来执行
  • 当一个线程无事可做,超过一定的时间(keepAliveTime)时,线程池会判断,如果当前运行的线程数大于 corePoolSize,那么这个线程就被停掉

综上所述,可知,线程池的所有任务完成后,它最终会收缩到 corePoolSize 的大小

以上就是个人理解的 线程池工作原理了

我是【辛勤de小蜜蜂】关注我,我们下期见


  • 由于博主才疏学浅,难免会有纰漏,假如您发现了错误或遗漏的地方,还望留言斧正,我会尽快对其加以修正。

  • 如果您觉得文章还不错,您的转发、分享、点赞、留言就是对我最大的鼓励。

  • 感谢您的阅读,十分欢迎并感谢您的关注。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

辛勤de小蜜蜂

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值