[Java][多线程]《Java多线程编程实战》—— 第9章 Thread Pool(线程池)模式

一个系统中的线程相对于其所要处理的任务而言,是一种非常有限的资源。线程不仅在执行任务时需要消耗CPU时间和内存等资源,线程对象(Thread实例)本身以及线程所需的调用栈(Call Stack)也占用内存,并且Java中创建一个线程往往意味着JVM会创建相应的依赖于宿主机操作系统的本地线程(Native Thread)。因此,为每个任务创建一个线程,通常是一种奢侈而不现实的事情。常见的做法是复用一定数量的线程,执行不断产生的任务。
Thread Pool模式的核心思想是使用队列等待处理的任务进行缓存,并复用一定数量的工作者线程去取队列中的任务进行执行。

类图

好处:

  • 减少线程创建的开销,提高响应性。//事先创建一部分线程。
  • 封装了工作者线程生命周期管理。
  • 减少销毁线程的开销

ThreadPoolExecutor 工作队列的选择:

  • 有界队列:限定线程池中待执行任务的数量。需要限定线程池最大大小为一个合理有限值。有界工作队列加上有限数量的工作者线程可能导致死锁。有界队列适合任务之间相互独立的情况。
  • 无界队列:工作队列本身不限制线程池中等待运行的任务的数量,任务数取决于任务本身对资源的使用情况。无界队列可能导致系统的不稳定
  • 直接交接队列(SynchronousQueue):实际上不使用缓存空间。需要限定线程池最大大小为一个合理有限值

线程池大小调校:
太大消耗资源,并增加上下文切换。
太小无法充分利用CPU资源,吞吐率过低。
合理的大小取决于任务的特性、系统资源状况以及任务所使用的稀缺资源状况等因素。
系统资源主要考虑CPU个数和JVM堆内存的大小。
任务特性主要考虑任务是CPU密集型、I/O密集型还是混合型。
CPU密集型Ncpu+1。一个额外的线程用于在缺页中断等待时使用CPU时间片。
I/O密集型核心线程池大小为1,最大线程池大小为2Ncpu。注意I/O操作会引起上下文切换,线程越多切换也越多。
混合型任务可将任务分解为CPU密集型和I/O密集型两种子任务,分别采用各自的线程池执行。
公式:(略)
S=NcpuUcpu(1+Wt/St)
稀缺资源如数据库连接等。
重要的是线程池的大小要可配置,上述建议可作为起点和依据。

线程泄露:线程池中的线程意外终止,使得线程池中实际可用的线程变少。
通常是由于线程对象的run方法中异常处理没有捕获RuntimeException和Error导致run方法意外返回。ThreadPoolExecutor已经对线程泄露进行了预防。

线程池饱和处理策略:
ThreadPoolExecutor.AbortPolicy:直接抛出异常
ThreadPoolExecutor.DiscardPolicy:丢弃而不抛出异常
ThreadPoolExecutor.DiscardOldestPolicy:丢弃最老的任务,重新尝试被拒绝的任务
ThreadPoolExecutor.CallerRunsPolicy:在客户端线程中执行被拒绝的任务
自定义:例如采用阻塞式入队方法(put)将提交失败的任务重新入队

死锁:适合提交给同一线程池实例执行的任务是相互独立的任务,而不是彼此有依赖关系的任务。否则可能导致死锁。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值