Java 线程池

本文详细探讨了Java线程池的基本概念、创建方法、设计原理及源码剖析。线程池通过池化思想减少线程创建销毁的开销,提高响应速度和系统稳定性。文章分析了ThreadPoolExecutor类的构造参数,如核心线程数、最大线程数、线程存活时间等,并介绍了四种拒绝策略。此外,文章阐述了线程池的工作流程、生命周期及其内部类Worker的运行机制,帮助读者深入理解线程池的使用和设计。
摘要由CSDN通过智能技术生成

为什么会想到写这篇文章呢,其实网上也挺多的,主要是这次和同学讨论到一个问题(就是线程工厂创建线程的时候,再次对runnable的run方法捕获异常),就涉及到线程池,而且有些疑问,然我去看一些博客的时候也写的不对,最后还是通过看源码解决自己的疑问。首先,这篇博客会分析一些源码,线程池的设计,然后要注意的一些事项。如果认真读完,保证有收获的。

 

基本概念

线程池是什么

线程池(Thread Pool)是一种基于池化思想管理线程的工具,经常出现在多线程服务器中,如MySQL 数据库连接池。通常,在我们创建线程和销毁线程的时候,都会带来性能的开销。然而线程池维护多个线程,等待监督管理者分配可并发执行的任务。这种做法,一方面避免了处理任务时创建销毁线程开销的代价,另一方面避免了线程数量膨胀导致的过分调度问题,保证了对内核的充分利用。

 

ThreadPoolExecutor

而本文描述线程池是JDK中提供的ThreadPoolExecutor类,看一下这个类图关系

 

顶层接口Executor提供了一种思想:将任务提交和任务执行进行解耦用户无需关注如何创建线程,如何调度线程来执行任务,用户只需提供Runnable对象,将任务的运行逻辑提交到执行器(Executor)中,由Executor框架完成线程的调配和任务的执行部分。

ExecutorService接口增加了一些能力:

(1)扩充执行任务的能力,补充可以为一个或一批异步任务生成Future的方法。

(2)提供了管控线程池的方法,比如停止线程池的运行。AbstractExecutorService则是上层的抽象类,将执 行任务的流程串联了起来,保证下层的实现只需关注一个执行任务的方法即可。

最下层的实现类ThreadPoolExecutor实现最复杂的运行部分,ThreadPoolExecutor将会一方面维护自身的生命周期,另一方面同时管理线程和任务,使两者良好的结合从而执行并行任务。

ThreadPoolExecutor提供了四个构造方法,可以自行进行参数配合,其主要目的的对方便用户根据业务自行控制。

 

线程池好处

  • 降低资源消耗:通过池化技术重复利用已创建的线程,降低线程创建和销毁造成的损耗。

  • 提高响应速度:任务到达时,无需等待线程创建即可立即执行。

  • 提高线程的可管理性:线程是稀缺资源,如果无限制创建,不仅会消耗系统资源,还会因为线程的不合理分布导致资源调度失衡,降低系统的稳定性。使用线程池可以进行统一的分配、调优和监控。

  • 提供更多更强大的功能:线程池具备可拓展性,允许开发人员向其中增加更多的功能。比如延时定时线程池ScheduledThreadPoolExecutor,就允许任务延期执行或定期执行。

 

线程池的创建

这里我们还是继续上面那张图吧,对JDK 1.8中ThreadPoolExecutor构造方法参数进行分析。

  • corePoolSize

    核心线程数,默认情况下核心线程会一直存活,即使处于闲置状态也不会受存keepAliveTime限制。除非将allowCoreThreadTimeOut设置为true。(这里注意下,即使没有任务,核心线程也不会被回收的,这个问题后面再源码分析中会讲到,先注意下)

  • maximumPoolSize

    线程池所能容纳的最大线程数。超过这个数的线程将被阻塞。当任务队列为没有设置大小的LinkedBlockingDeque时,这个值无效,也就是说如果线程数大于核心了,那么就要放到阻塞队列中去,如果阻塞队列满了的话,才会考虑线程扩容,当超过最大线程数的话,就需要想要的拒绝策略了。(这里也会在源码进行分析,先记住就好

  • keepAliveTime

    非核心线程的闲置超时时间,超过这个时间就会被回收。

  • unit

    这个就不多说,就是时间单位。

  • workQueue

    线程池中的任务队列,常用的有三种队列:SynchronousQueue,LinkedBlockingDeque,ArrayBlockingQueue,要注意的是这个是阻塞队列,为什么是阻塞呢,其实有用意的,后面再源码中会提到。

    队列 描述
    ArrayBlockingQueue 这是一个基于数组阻塞队列,有界的,容量自行传入,按照FIFO的原则
    LinkedBlockingQueue 基于链表的阻塞队列,按照FIFO原则,有界的,容量可自行传入。注意:这个队列的最大长度是Integer.MAX_VALUE,所以用这个可能有OOM风险。
    LinkedBlockingDeque 基于链表结构实现的双向阻塞队列
    PriorityBlockingQueue 支持任务按照优先级排序,默认是自然排序的,当然也可以自定义任务,实现Runnable 和 Comparable接口来自定义排序。
    DelayQueue 延迟获取队列,在创建元素的时候,可以指定多久才能从队列中获取。
    SynchronousQueue 一个不存储元素的阻塞队列,每一个put操作必须等待take操作,否则不能添加元素。

     

  • threadFactory

    看名字就很明显,线程工厂,工厂那么肯定是用来创建的了,这里就是用来给这个线程池创建线程的,也就是说,线程池需要并且可以新建线程的时候,就会通过该工厂了进行创建。工厂统一要实现java.util.concurrent.ThreadFactory这个接口,可以看下jdk提供的默认线程工厂实现。io.netty.util.concurrent.DefaultThreadFactory

    //这里要注意的一个东西就是 方法参数的这个r,后面在源码中也会提到
    @Override
    public Thread newThread(Runnable r) {
            // 这里就是用来创建线程,其实在这个地方,我们可以对线程进行分组命名,方便查看
            Thread t = newThread(new DefaultRunnableDecorator(r), prefix +                                 nextId.incrementAndGet());
            try {
                //是否为守护线程设置
                if (t.isDaemon()) {
                    if (!daemon) {
                        t.setDaemon(false);
                    }
                } else {
                    if (daemon) {
                        t.setDaemon(true);
                    }
                }
                //优先级设置,这个我没去具体分析,猜错应该是线程调度的一个优先级问题
                if (t.getPriority() != priority) {
                    t.setPriority(priority);
                }
            } catch (Exception ignored) {
            }
            return t;
        }
  • RejectedExecutionHandler

    拒绝策略,这个是什么时候用到呢,就是线程池任务繁忙,不能再接收任务的时候,就会有相应的拒绝回应。

    如果生产环境下,遇到这种问题,可能就像调整参数,那么该如何去动态修改呢,这是个问题,先留着吧?

    看下

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值