java并发编程笔记--ThreadPoolExecutor实现

阅读全文请点击

 ThreadPoolExecutor是jdk自带的线程池实现。看到了"池"一定会想到对象池模式,它是单例模式的一个变种,主要思想是通过共享复用已有的空闲对象,达到限制开销提高性能的目的。这里的对象可以理解为某种"资源",比如:数据库连接、线程、socket连接...创建这种资源的消耗比较大,如果每次使用都新建的话,会造成额外的开销。同时因为对资源的创建没有限制,可能会重复创建大量的对象,导致资源枯竭。如果能够提前创建好一批对象放到一个"池"中,每次使用时都从池中选取空闲的对象,用完后再还入池中,这样便提高了资源的使用效率,又因为限制了创建对象的数量,也可以避免大量创建对象导致资源枯竭。我们常用的线程池、DB连接池、socket连接池等都是用的这个思路。

适合使用线程池的场景是建立在如下基础上的:
0)串行执行的响应性和吞吐量无法满足需求:串行程序会在一个线程中逐处理所有任务,因此可能受任务执行时间的长度影响,具有较差的响应性。同时,因为单线程程序往往不能充分利用CPU的资源,无法达到最优的吞吐量。
1)线程的生命周期管理(时间)开销很高:因为创建、销毁线程开销很大,所以频繁的创建线程会造成较大的时间开销,从而减慢任务的处理速率,增加延迟。
2)物理资源的消耗:活跃的线程会消耗操作系统资源,包括:内存、文件句柄等。如果应用程序中存在大量活跃的线程,将会占用大量的资源,有可能导致系统资源枯竭。 
3)稳定性:随意的创建线程而不加管控,不利于整个应用程序的稳定。
综上所述,使用多线程提升并发应该是使用合适的线程数达到最好的性能即可,并非线程越多越好。一些场景下(如任务规模很小或者资源需要串行访问的场景),单线程处理可能比多线程更简单且性能更好。

线程池的组成

抛开ThreadPoolExecutor的实现,我们先自己设想一下线程池的概念模型:

image

    首先我们需要一个用于全局管控线程池运行的类,负责线程池的创建、任务的运行以及最后线程池关闭...完整流程的管理,这个类构成了线程池的主体,即线程管理类。线程池中包含多个用于执行任务的工作线程,工作线程由线程管理类创建和维护,能够执行外部提交的任务,并响应取消任务的操作。

    由于不同的业务场景创建的线程具有不同的特征,比如:以相同的前缀命名,设置为守护线程,归属于相同的ThreadGroup等...这些参数的配置对于线程池而言是重复性工作,因此单独抽取一个线程工厂类用作工作线程的创建,由于不同的场景设置的线程参数不同,因而需要客户端自己根据需求定制,故抽象为接口。

    线程池中使用阻塞队列作为任务提交和任务执行的"缓冲",当工作线程忙不过来时,线程池可以先将任务存放到一个任务队列中,待后续有工作线程空闲时,再从队列中获取新的任务执行。由于有了任务队列这个"缓冲",便增加了执行任务的灵活度,任务队列可以根据不同的策略对其中的任务进行编排,比如按优先级执行、延迟执行...存在这灵活性,就意味着要为客户端留有扩展的余地,因而任务队列也抽象为接口。

    线程池作为生产者-消费者模式的实现,也要考虑生产速率和消费速率的平衡。当生产-消费失衡时,即任务的提交速率大于消费速率时,为了保障程序的稳定性,需要对任务做驳回处理,以便降低线程池的负载。同时,为了减少驳回任务造成的损失,需要对被驳回的任务提供必要的驳回处理策略。最简单快速且合理的驳回策略便是抛出异常,即我们在服务降级中常用的快速失败策略,为了避免处理驳回任务带来额外的负载压力,直接放弃任务执行,通过抛出异常告知客户端任务失败。当然,为了满足各式各样的业务场景,也需要留给客户端定制策略的余地,因此驳回策略也被抽象为接口。


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值