并发多线程☞线程池基本概念

线程池

概念

线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务。

作用

  • 线程的创建需要开辟虚拟机栈,本地方法栈、程序计数器等线程私有的内存空间。线程池减少了创建和销毁线程的次数,降低系统开销。
  • 利用线程池管理并复用线程、控制最大并发数等,方便管理。
  • 提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
  • 隔离线程环境。

线程池模式

  • HS/HA半同步/半异步模式
    半同步/半异步模式又称为生产者消费者模式,是比较常见的实现方式,比较简单。分为同步层、队列层、异步层三层。同步层的主线程处理工作任务并存入工作队列,工作线程从工作队列取出任务进行处理,如果工作队列为空,则取不到任务的工作线程进入挂起状态。由于线程间有数据通信,因此不适于大数据量交换的场合。
  • L/F领导者与跟随者模式
    在线程池中的线程可处在3种状态之一:领导者leader、追随者follower或工作者processor。任何时刻线程池只有一个领导者线程。事件到达时,领导者线程负责消息分离,并从处于追随者线程中选出一个来当继任领导者,然后将自身设置为工作者状态去处置该事件。处理完毕后工作者线程将自身的状态置为追随者。这一模式实现复杂,但避免了线程间交换任务数据,提高了CPU cache相似性。在ACE(Adaptive Communication Environment)中,提供了领导者跟随者模式实现。

线程池的伸缩性对性能的影响

  • 创建太多线程,将会浪费一定的资源,有些线程未被充分使用。
  • 销毁太多线程,将导致之后浪费时间再次创建它们
  • 创建线程太慢,将会导致长时间的等待,性能变差。
  • 销毁线程太慢,导致其它线程资源饥饿。

基本参数

  • corePoolPoolSize:线程池长期维持的核心线程数
  • maximumPoolSize:线程池的线程数的上限
  • keepAliveTime:空闲线程的存活时间
  • unit:时间单位,和keepAliveTime配合使用
  • workQueue:工作队列,默认SynchronousQueue
  • threadFactory:线程的生成方式,默认工厂
  • handler:拒绝策略,默认拒绝策略

三种工作队列

  • SynchronousQueue:直接提交
    是工作队列的默认选项,将任务直接提交给线程而不保持。
    如果不存在可用于立即运行任务的线程,则试图把任务加入队列将失败,因此会构造一个新的线程。
    特点

     1、可以避免在处理可能具有内部依赖性的请求集时出现锁。
     2、直接提交通常要求无界maximumPoolSizes 以避免拒绝新提交的任务。
     3、当命令以超过队列所能处理的平均数连续到达时,此策略允许无界线程具有增长的可能性。
    
  • LinkedBlockingQueue:无界队列
    特点

     1、在所有核心线程都忙时,新任务在队列中等待。
     2、仅创建corePoolSize线程即可。(maximumPoolSize的值没有任何作用。)
     3、当每个任务完全独立于其他任务时,任务不会影响彼此的执行。
    
  • ArrayBlockingQueue:有界队列
    特点

     1、有界队列有助于防止资源耗尽,但是调整和控制起来会更加困难,队列大小和最大池大小可以相互权衡
     2、使用大队列和小池可以最大程度地减少CPU使用率,操作系统资源和上下文切换开销,但可能导致人为地降低吞吐量。
     3、如果任务频繁阻塞(例如如果它们是受I/O约束的),则系统可能可以为非预定的更多线程安排时间。
     4、使用小队列通常需要更大的池大小,这会使CPU繁忙,但可能会遇到不可接受的调度开销,这也会降低吞吐量。
    

四种拒绝策略

  • AbortPolicy:处理程序遭到拒绝将抛出运行时 RejectedExecutionException
  • DiscardPolicy:不能执行的任务将被删除
  • DiscardOldestPolicy:如果执行程序尚未关闭,则位于工作队列头部任务将被删除,然后重试执行程序(如果再次失败,则重复此过程)
  • CallerRunsPolicy:线程调用运行该任务的 execute 本身。此策略提供简单的反馈控制机制,能够减缓新任务的提交速度。

线程池的种类

种类作用特征
newCachedThreadPool创建一个可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们,并在需要时使用提供的 ThreadFactory 创建新线程。(1)线程池中数量没有固定,可达到最大值(Interger. MAX_VALUE)
(2)线程池中的线程可进行缓存重复利用和回收(回收默认时间为1分钟)
(3)当线程池中,没有可用线程,会重新创建一个线程
newFixedThreadPool创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程。
在任意点,在大多数 nThreads 线程会处于处理任务的活动状态。如果在所有线程处于活动状态时提交附加任务,则在有可用线程之前,附加任务将在队列中等待。
如果在关闭前的执行期间由于失败而导致任何线程终止,那么一个新线程将代替它执行后续的任务(如果需要)。
在某个线程被显式地关闭之前,池中的线程将一直存在。
(1)线程池中的线程处于一定的量,可以很好的控制线程的并发量
(2)线程可以重复被使用,在显示关闭之前,都将一直存在
(3)超出一定量的线程被提交时候需在队列中等待
newSingleThreadExecutor创建一个使用单个 worker 线程的 Executor,以无界队列方式来运行该线程。
(注意,如果因为在关闭前的执行期间出现失败而终止了此单个线程,那么如果需要,一个新线程将代替它执行后续的任务)。
可保证顺序地执行各个任务,并且在任意给定的时间不会有多个线程是活动的。与其他等效的 newFixedThreadPool(1) 不同,可保证无需重新配置此方法所返回的执行程序即可使用其他的线程。
(1)线程池中最多执行1个线程,之后提交的线程活动将会排在队列中以此执行
newScheduleThreadPool创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行。(1)线程池中具有指定数量的线程,即便是空线程也将保留
(2)可定时或者延迟执行线程活动
newSingleThreadScheduledExecutor创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行。(1)线程池中具有指定数量的线程,即便是空线程也将保留
(2)可定时或者延迟执行线程活动

线程池的工作流程

1、线程池刚创建时,里面没有一个线程。任务队列是作为参数传进来的。不过,就算队列里面有任务,线程池也不会马上执行它们。
2、当调用 execute() 方法添加一个任务时,线程池会做如下判断:

1)、如果正在运行的线程数量小于 corePoolSize,那么马上创建线程运行这个任务;
2)、如果正在运行的线程数量大于或等于 corePoolSize,那么将这个任务放入队列;
3)、如果这时候队列满了,而且正在运行的线程数量小于 maximumPoolSize,那么还是要创建非核心线程立刻运行这个任务;
4)、如果队列满了,而且正在运行的线程数量大于或等于 maximumPoolSize,那么线程池会抛出异常RejectExecutionException。

3、当一个线程完成任务时,它会从队列中取下一个任务来执行。
4、当一个线程无事可做,超过一定的时间(keepAliveTime)时,线程池会判断,如果当前运行的线程数大于 corePoolSize,那么这个线程就被停掉。所以线程池的所有任务完成后,它最终会收缩到 corePoolSize 的大小。

线程池的实现原理

线程池的实现过程没有用到Synchronized关键字,用的都是Volatile,Lock和同步(阻塞)队列,Atomic相关类,FutureTask等等,因为后者的性能更优。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值