线程池详解

一. 线程池的好处

  1. 降低资源消耗。通过重复利用已创建的线程,降低线程创建和销毁造成的消耗。
  2. 提高响应速度。当任务到达时,任务可以不需要等待线程的创建就能直接执行。
  3. 增加线程的可管理性。线程是稀缺资源,使用线程池进行同一个的分配,调优和监控。

二. 线程池的七个参数

线程池的七个参数

  1. corePoolSize(线程池的基本大小):线程池维护的一个最小线程数量。一旦创建出来,只要不大于corePoolSize数量,就算线程处于空闲也不会被销毁。这个参数就是线程池的最小线程数量。

  2. maximumPoolSize(线程池最大数量):一个任务被提交到线程池后,首先会找到空闲存活的线程,如果有,则直接将任务交给空闲线程来执行;如果没有,则会缓存到工作队列中,如果工作队列满了,才会创建一个新的线程,新创建的线程会直接执行新加入的任务,然后再去找工作队列的头部任务,但是线程池不会无限的创建线程,它会有一个最大的线程数量,这个参数就是决定线程池创建的线程最大数量。

  3. keepAliveTime(空闲线程存活时间):如果线程处于空闲状态,且当前线程数量大于corePoolSize的最小线程数量,那么过了这个参数指定的时间后,这些空闲的线程就会被销毁。

  4. unit(空闲线程存活的单位):就是keepAliveTime的时间单位。

  5. workQueue(工作队列):任务被提交后,如果线程数量大于corePoolSize,则会先进入工作队列中,任务调度时再从队列中取出,jdk提供四种工作队列

    • ArrayBlockingQueue:基础数组的有界阻塞队列,按FIFO排序。当任务被提交后,如果线程数量大于等于corePoolSize,则会将任务放入队尾,等待被调度。如果队列满了,如果线程数量少于maximumPoolSize则会创建新的线程,拿取队头的任务执行;如果队列满了,且线程数量到达maximumPoolSize,则会执行拒绝策略。
    • LinkBlockingQueue:基于链表的无界阻塞队列(默认最大容量是Interger.MAX),按FIFO排序,由于队列近似无界,所以当线程池中的线程数量大于corePoolSize后,再有新任务进来时,会一直加入工作队列,而不会创建新的线程,所以该工作队列,maximumPoolSize没有作用。
    • SynchronousQueue:一个不缓存任务的阻塞队列,生产者放入任务就必须等待消费者取出任务。如果没有可用的线程,则会创建新地新城,如果线程数量达到maxPoolSize,则会执行拒绝策略。
    • PriorityBlockingQueue:一个具有优先级的无限阻塞队列。
  6. threadFactory(线程工厂):创建一个新线程时用的工厂,可以用来设定线程名。

  7. handler(拒绝策略):当工作队列的任务达到最大,并且线程池无法再创建新的线程取出任务的时候,则会执行拒绝策略,策略默认是AbortPolicy。jdk提供4种拒绝策略

    • CallerRunsPolicy:在该策略下,调用者线程中直接执行被拒绝任务的run方法,除非线程池已经shutdown,则直接抛弃任务。
      在这里插入图片描述
    • AbortPolicy:在该策略下,直接丢弃任务,并抛出RejectedExecutionException异常。
      在这里插入图片描述
    • DiscardPolicy:在该策略下,直接丢弃任务,什么都不做。
      在这里插入图片描述
    • DiscardOldestPolicy:在该策略下,抛弃进入队列最早的那个任务,也就是第二个任务,然后尝试把这次拒绝的任务放入队列。
      在这里插入图片描述
      在这里插入图片描述

三. Executors 4种创建线程池的方法

  1. newFixedThreadPool:固定线程数量的线程池。corePoolSize = maximumPoolSize, keepAliveTime为0,工作队列使用无界的LinkedBlockingQueue。使用于为了满足资源管理的需求,而需要限制当前线程数量的场景,适用于负载比较重的服务器。
    在这里插入图片描述
  2. newSingleThreadExecutor:只有一个线程的线程池。corePoolSize = maximumPoolSize = 1,keepAliveTime为0,工作队列使用无界的LinkedBlockingQueue。适用于需要保证循序的执行各个任务的场景。
    在这里插入图片描述
  3. newCachedThreadPool:按需要创建新线程的线程池。核心线程数为0,最大线程数为Integer.MAX_VALUE,keepAliveTime为60秒,工作队列使用同步移交SynchronousQueue。该线程池可以无限扩展,当需求增加时,可以添加新的线程,而当需求降低时会自动回收空闲线程。适用于执行很多的短期异步任务,或者是负载较轻的服务器。
    在这里插入图片描述
  4. newScheduledThreadPool:创建一个以延迟或定时的方式啦执行任务的线程池,工作队列是为DelayedWorkQueue,适用于需要多个后台线程执行周期任务。
    在这里插入图片描述

三. 线程池的5种状态

线程池的五种状态

  • RUNNING:接受任务并处理排队的任务。
  • SHUTDOWN:不接受新任务,但处理排队的任务。
  • STOP:不接受新任务,不处理排队的任务,并中断正在进行的任务。
  • TIDYING:所有任务都已终止,workerCount为零,线程转换到TIDYING状态将运行terminated()钩子方法。
  • TERMINATED:terminated()已完成。

1. 状态之间的转换

在这里插入图片描述

2. 线程池ctl属性的底层含义

既然说到了线程池的状态,必然少不了其底层状态和线程数量是如何存储的。

ctl是一个打包两个概念字段的原子整数。

  1. workerCount:指示线程的有效数量;
  2. runState:指示线程池的运行状态,有RUNNING,SHUTDOWN,STOP,TIDYING,TERMINATED状态。

int有32位,其中ctl的低29位用于表示workerCount,高3位表示runState。

在这里插入图片描述
下面我们看看源码:

private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
private static final int COUNT_BITS = Integer.SIZE - 3; // 29
private static final int CAPACITY   = (1 << COUNT_BITS) - 1;// 代码块1

// runState is stored in the high-order bits
private static final int RUNNING    = -1 << COUNT_BITS;
private static final int SHUTDOWN   =  0 << COUNT_BITS;
private static final int STOP       =  1 << COUNT_BITS;
private static final int TIDYING    =  2 << COUNT_BITS;
private static final int TERMINATED =  3 << COUNT_BITS;

// Packing and unpacking ctl
private static int runStateOf(int c)     { return c & ~CAPACITY; }//代码块2
private static int workerCountOf(int c)  { return c & CAPACITY; }//代码块3
private static int ctlOf(int rs, int wc) { return rs | wc; }//代码块4

为什么状态占3位呢?
因为5种状态,5个常数分别左移29位,符号位占一位,2,3占两位,要想完整的表示5种状态,就得用3位。

代码块1表示:1左移29位后是 ,10 0000 0000 0000 0000 0000 0000 0000 ,-1 等于29个1,

代码块2表示:由代码1可知,CAPACITY等于29个1,按位取反后,低29位全为0,高3位为1,然后跟ctl与运算,可以得到runState的信息。也就是高3位。

代码块3表示:根据代码1知道,低29位全为1,则与运算得到workerCount的值。

代码块4表示:将workerCount或上runState得到stl。

### Java 线程池详解 #### 创建线程池的方式 Java 提供了几种创建线程池的方法,最常用的是通过 `Executors` 工厂类来获取预配置好的线程池实例。然而,在实际项目中更推荐使用 `ThreadPoolExecutor` 构造器来自定义参数,以获得更好的灵活性和性能控制[^1]。 ```java // 不推荐的做法:固定大小的缓存线程池可能导致资源浪费或耗尽 ExecutorService cachedPool = Executors.newCachedThreadPool(); // 推荐做法:自定义 ThreadPoolExecutor 参数设置 ThreadFactory namedThreadFactory = new ThreadFactoryBuilder().setNameFormat("demo-pool-%d").build(); ThreadPoolExecutor executor = new ThreadPoolExecutor( corePoolSize, maximumPoolSize, keepAliveTime, TimeUnit.SECONDS, workQueue, namedThreadFactory); ``` #### 关键组件说明 - **核心线程数 (`corePoolSize`)** 定义了即使处于空闲状态也会被保留在线程池中的最小线程数量。 - **最大线程数 (`maximumPoolSize`)** 设置允许的最大活动线程数目;当现有任务队列已满而又有新的提交任务时才会触发扩容至该上限。 - **存活时间 (`keepAliveTime`)** 非核心线程闲置后的自动回收等待周期长度。 - **阻塞队列 (`workQueue`)** 存储待处理的任务对象集合,不同类型的队列会影响吞吐量表现及拒绝策略行为。 - **线程工厂 (`threadFactory`)** 负责生产新线程实体,默认实现较为简单,通常建议开发者根据应用场景定制化命名规则以便于调试跟踪。 #### 常见错误与优化技巧 忽视对线程池内部运行状况监控是常见的失误之一。应当定期审查并调整相关参数,确保其适应当前负载需求变化趋势。另外需要注意捕获未预见异常情形下的恢复机制设计,防止因个别失败案例影响整体服务稳定性[^2]。 #### 实践指南 对于Web容器如Tomcat而言,内置有经过优化过的专用线程管理模块用于支撑高并发请求场景下高效运作的要求。尽管如此,了解基础概念仍然有助于更好地理解框架底层运作机理,并能在必要时候做出针对性调优措施。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值