线程池讲解

为什么要用线程池

  1. 降低资源消耗,提高响应速度
    池化将线程统一在一起,避免了创建/释放线程的损耗
  2. 提高线程可管理性,合理配置资源
    防止对资源的无限申请,对线程合理的调度及监控
  3. 线程池具有可扩展性

线程池结构

在这里插入图片描述
这里简单说明一下:线程池如何将任务与线程分离。实际上,线程池内部采用生产者/消费者模式。生产者是任务管理,消费者是任务执行。
在这里插入图片描述

构造函数

    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        ...
    }
参数作用
corePoolSize核心线程数
maximumPoolSize最大线程数
workQueue任务队列
keepAliveTime空闲线程保活时间
ThreadFactory提供线程
handler线程池满但有新任务提交时策略

线程池生命周期

线程池中有两个重要参数管理线程池的生命周期:线程池运行状态runState 和 线程数量workerCount,这两个参数在同一变量的不同位上

private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));

高3位保存runState,低29位保存workerCount。这样一次CAS,设置两个变量,相比每个变量一个CAS,岂不双倍快乐。
线程池的运行状态有五种

运行状态说明
RUNNING可以提交新的任务,也能处理阻塞队列
SHUTDOWN线程池处于关闭状态,可以提交新的任务 ,也能处理阻塞队列
STOP可以提交新的任务,也能处理阻塞队列,中断当前线程
TIDYING所有任务都已终止,有效线程数为0
TERMINATED调用terminated()之后进入该状态

在这里插入图片描述

任务执行机制

这里强调线程池如何管理任务。线程池中任务和线程解耦,二者互不相关。线程池安排任务的调度,将任务分配给线程池中的线程

任务调度

public void execute(Runnable command)

在这里插入图片描述

If a task can be successfully queued, then we still need to double-check whether we should have added a thread (because existing ones died since last checking) or that the pool shut down since entry into this method. So we recheck state and if necessary roll back the enqueuing if stopped, or start a new thread if there are none.
即使成功加入阻塞队列,在申请新的线程时仍要二次检查,有可能线程在上一次检查后挂了,也有可能线程池在上一次检查后停了。所以我们要重新检查状态,并在必要的时候让Task重新排队,或是开启新的线程

任务缓冲

当线程池来不及处理任务时,会将其暂时存放在阻塞队列中,等候处理。

使用不同的阻塞队列可以实现不一样的任务存取策略。线程池支持的队列模式有:

	null

从我们使用线程池的出发点:对资源的合理管控 来说,最好不要使用不限长的阻塞队列,如果有过多的任务申请,可能导致资源空间不限制的被占用,影响系统稳定性

任务申请

private Runnable getTask() 

任务执行存在两种可能,一种是新建线程执行,另一种是已有线程从阻塞队列中获取。这里对第二种情况进行说明,任务申请时,线程池将严格控制工作线程的数量
在这里插入图片描述

任务拒绝

拒绝策略是一个接口

public interface RejectedExecutionHandler {
    void rejectedExecution(Runnable r, ThreadPoolExecutor executor);
}

用户可以通过实现这个接口去定制拒绝策略,也可以选择JDK提供的四种已有拒绝策略

线程执行过程

这部分强调线程池如何管理线程,并维护线程在线程池中生命周期(不同于线程本身的生命周期)

线程添加

private boolean addWorker(Runnable firstTask, boolean core) 
  • firstTask : 新增线程执行的第一个任务
  • core :  true   以corePoolsize为容量
                  false   以maximumPoolSize为容量
    在这里插入图片描述

线程执行任务

final void runWorker(Worker w)

在这里插入图片描述

线程回收

线程池中线程回收依赖JVM实现,线程池中只维护线程的引用,若线程可回收时,删除引用,等待JVM释放

线程池的执行过程
在这里插入图片描述

应用

  1. 快速响应
    响应优先,需要把用户发起的响应尽可能快的发送,故不应当设置消息队列,且corePoolsizemaxPoolSize尽可能大
  2. 快速处理批量业务
    吞吐量优先,要求最大限度利用有限资源 (因为肯定快速响应不完,正所谓 穷则战术穿插,达则给老子炸,要是能一下处理完还用受这委屈?再说了,反正处理不完,只要别慢的太离谱就行,留几个干别的别人也看不出来)
    需要合理设置阻塞队列、最大线程数和核心线程数,避免过多上下文切换影响速度

动态化线程池设计

  1. 动态调参
    能根据情况调整线程数量 队列的长度,避免线程池资源不足/资源过剩
  2. 任务监控
    监控任务的执行情况,记录任务的执行时间及平均时间,用于优化任务的合并于拆分
  3. 负载警告
    能在队列积压过多/线程池频繁高负载是发出警告
  4. 修改记录
    记录每次修改位置及修改原因,便于回滚
  5. 权限校验
    防止无关/恶意人员对线程池破坏

参考链接:

  1. Java线程池实现原理及其在美团业务中的实践 - 美团技术团队的文章 - 知乎
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
线程池是一种常见的并发编程技术,可以提高程序的性能和响应速度。它通过创建一定数量的线程,并将任务分配给这些线程来执行,从而减少线程的创建和销毁所带来的开销,提高系统的效率和稳定性。 线程池的基本构成包括任务队列、工作线程池和管理器。任务队列用于存储待执行的任务,工作线程池用于执行任务,管理器用于管理线程池的状态和任务分配。 在线程池启动时,会创建一定数量的工作线程,并将它们放入空闲队列中。当有任务提交时,管理器将任务添加到任务队列中,空闲线程从队列中取出任务并执行。当所有的线程都在执行任务时,新的任务将被暂存到任务队列中,等待空闲线程的出现。 线程池的优点是可以避免线程创建和销毁的开销,提高应用程序的性能和响应速度;可以控制线程的数量和执行状态,避免线程过多或过少所带来的问题;可以通过合理的任务分配和调度,提高系统的效率和稳定性。 线程池的缺点是需要占用一定的系统资源,包括内存和CPU资源;需要对任务的执行时间和线程的数量进行合理的配置,否则可能会导致系统的瓶颈和性能下降;需要对任务队列的大小和任务的优先级进行合理的设置,否则可能会导致任务执行的不公平和延迟。 总之,线程池是一种常见的并发编程技术,可以提高系统的效率和稳定性,但需要合理的配置和设计,才能发挥其最大的优势。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值