Java中线程池的详解以及各个参数的含义

什么是线程池

        Java中的线程池是通过java.util.concurrent.ThreadPoolExecutor类实现的,它是Executor框架中的一个核心组件,用于管理和控制线程的创建、执行和销毁。线程池的主要优势在于能够复用线程,减少线程创建和销毁的开销,同时还可以根据系统的负载动态调整线程数量,以达到优化资源使用和提升系统响应性的目的。

        线程池中的继承关系

        

Executor: 所有线程池的接口,只有一个方法。

ExecutorService: 增加Executor的行为,是Executor实现类的最直接接口。

Executors: 提供了一系列工厂方法用于创先线程池,返回的线程池都实现了ExecutorService 接口。

ThreadPoolExecutor:线程池的具体实现类,一般用的各种线程池都是基于这个类实现的。

线程池的底层原理

        基于任务调度和资源复用的设计思路,其核心目标在于高效管理和执行大量短期异步任务,同时减少系统开销和提升响应速度

        预先创建线程->任务队列管理->动态线程调整->任务分配与执行->拒绝策略->线程复用

        预先创建线程:预先创建队列,提前布置,就比如说类似于我们去银行取钱,提前预留一些窗口

        任务队列管理:提交到线程池的任务会被放置到一个内部的任务队列中。这个队列可以是阻塞队列,如ArrayBlockingQueueLinkedBlockingQueue,就比如说,银行这一时间段的客人太多了,就会安排取号排队。如果人是在太多了,可以采取关闭或者说在门口外排队。或者直接关门,不接受其他的客人来办理业务。

  动态线程调整:线程池会根据任务量动态调整工作线程的数量。当任务队列开始积压且当前线程数小于最大线程数(maximumPoolSize)时,线程池会创建新的线程来处理队列中的任务。就比如来的人实在是太多了,银行已经忙不过来了,就会多开设几个窗口进行服务。

        任务分配与执行: 线程池维护了一个机制来分配队列中的任务给可用线程。一旦有线程空闲,它会从队列中取出下一个任务并执行。这类似于银行客户从候客区被分配到窗口办理业务。

        线程复用: 通过重用线程,线程池减少了频繁创建和销毁线程的开销,这对于性能敏感的应用特别重要。线程创建是昂贵的操作,因为它涉及到与操作系统的交互和资源分配。这就类似于,银行开设一个窗口后,就会一直在服务,不会服务完一单后,窗口就会关闭。

参数

  1. corePoolSize(核心线程数):

    • 这是线程池中常驻线程的数量。即使线程处于空闲状态,只要线程池中的线程数不超过corePoolSize,这些线程也会被保留。只有在超过这个数量并且线程空闲时间超过keepAliveTime时,多余的线程才会被终止。
  2. maximumPoolSize(最大线程数):

    • 线程池允许同时存在的最大线程数。当活动线程数达到corePoolSize后,新任务将会被放入队列中排队等待,直到线程池中的线程数量达到maximumPoolSize,这时如果还有新的任务提交,而队列也已满,那么线程池可能会拒绝处理这个任务,或者调用RejectedExecutionHandler(拒绝策略)来处理。
  3. keepAliveTime(空闲线程存活时间):

    • 如果线程池中的线程数量超过了corePoolSize,那么多余的空闲线程在等待新任务到达的最长时间。超过这个时间后,空闲线程会被终止,直到线程池中的线程数不超过corePoolSize
  4. unit(时间单位):

    • 用来指定keepAliveTime的时间单位,例如TimeUnit.SECONDSTimeUnit.MILLISECONDS等。
  5. workQueue(工作队列):

    • 用于保存等待执行的任务的阻塞队列。不同的队列实现(如ArrayBlockingQueueLinkedBlockingQueueSynchronousQueue等)会影响线程池的行为和性能。
  6. threadFactory(线程工厂):

    • 用于创建新线程的工厂。可以用来设置线程的名称、优先级、是否为守护线程等属性。
  7. handler(拒绝策略):

    • 当线程池和工作队列都达到饱和状态,即无法再接受新任务时所采取的策略。JDK内置了四种拒绝策略:
      • AbortPolicy:抛出异常,拒绝新任务。
      • CallerRunsPolicy:调用者运行任务(谁提交任务谁就自己运行这个任务)。
      • DiscardPolicy:直接丢弃任务,不抛出异常。
      • DiscardOldestPolicy:丢弃队列中最旧的任务,然后尝试重新提交当前任务

线程数设置多少合适 

        设置线程池的线程数并没有一个固定的“最佳”值,因为它取决于多个因素,包括但不限于任务类型(CPU密集型、IO密集型或混合型)、系统环境(如CPU核心数、内存大小)、任务的性质(如是否可并行化)、以及应用的具体性能需求

        

  1. CPU密集型任务

    • 这类任务主要受限于CPU的计算能力,过多的线程会导致上下文切换频繁,反而降低效率。一般建议设置线程数为CPU核心数+1或接近CPU核心数,因为超线程可以在一定程度上提高CPU利用率,但过多线程会造成资源竞争和上下文切换的开销。
  2. IO密集型任务

    • 因为这类任务在执行过程中会有大量的等待时间(如文件读写、网络通信),可以设置更多的线程来充分利用CPU空闲时间。一个常用的公式是CPU核心数*2,但实际应用中可能需要根据具体IO操作的等待时间和CPU利用率进行调整。
  3. 混合型任务

    • 对于既包含CPU密集型又包含IO密集型任务的应用,可以考虑使用多个线程池分别处理不同类型的任务,或者采用更为复杂的动态调整策略,根据实时负载来动态调整线程池的大小。

使用线程池的好处

1、降低资源消耗。重复利用已创建线程,降低线程创建与销毁的资源消耗。

2、提高响应效率。任务到达时,不需等待创建线程就能立即执行。

3、提高线程可管理性。

4、防止服务器过载。内存溢出、CPU耗尽

 

  • 32
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Java线程池是一种用于管理和复用线程的机制,它可以提高多线程程序的性能和效率。在Java线程池由ThreadPoolExecutor类实现,通过设置不同的参数可以对线程池的行为进行调整。 以下是Java线程池的一些常用参数及其解释: 1. corePoolSize(核心线程数):线程池始终保持的活动线程数,即使它们处于空闲状态。当有新任务提交时,如果活动线程数小于corePoolSize,则会创建新线程来处理任务。 2. maximumPoolSize(最大线程数):线程池允许存在的最大线程数。当活动线程数达到maximumPoolSize并且工作队列已满时,新任务将会被拒绝。 3. keepAliveTime(线程空闲时间):当线程池的线程数量超过corePoolSize时,多余的空闲线程在等待新任务到来时的最长等待时间。超过这个时间,空闲线程将被终止。 4. unit(时间单位):keepAliveTime的时间单位,可以是秒、毫秒、微秒等。 5. workQueue(工作队列):用于存储等待执行的任务的阻塞队列。常见的工作队列有ArrayBlockingQueue、LinkedBlockingQueue、SynchronousQueue等。 6. threadFactory(线程工厂):用于创建新线程的工厂类。可以自定义线程的名称、优先级等属性。 7. handler(拒绝策略):当线程池无法接受新任务时的处理策略。常见的拒绝策略有AbortPolicy(默认,抛出RejectedExecutionException异常)、CallerRunsPolicy(由调用线程执行任务)、DiscardPolicy(直接丢弃任务)和DiscardOldestPolicy(丢弃最旧的任务)。 这些参数可以根据实际需求进行调整,以达到最佳的线程池性能和资源利用率。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值