线程池总结

线程池

1、什么时候使用线程池?

  • 单个任务处理时间比较短
  • 需要处理的任务数量很大

2、线程池优势

  • 重用存在的线程、减少线程创建、消亡的开销、提高性能
  • 提高响应速度
  • 提高线程的可管理性

3、线程池重要属性

  • ctl:记录当前线程池的运行状态和线程池内有效线程的数量,高3位runState,低29位保存workerCount
  • runStateOf:当前线程的状态
  • workerCountOf:工作线程的数量

4、线程池五种状态

  • RUNNING:能接受新提交的任务,并且也能处理阻塞队列中的任务
  • SHUTDOWN:关闭状态,不再接受新提交的任务,但却可以继续处理阻塞队列中已保存的任务
  • STOP:不能接受新任务,也不处理队列中的任务,会中断正在处理任务的线程
  • TIDYING:如果所有的任务都已终止了,workerCount (有效线程数) 为0,会去调用terminated()钩子函数
  • TERMINATED:在terminated() 方法执行完后进入该状态,默认terminated()方法中什么也没有做

5、工具类

ThreadPoolExecutor

  • corePoolSize:核心线程数量
  • maximumPoolSize:最大线程数量(默认Integer.MAX)
  • workQueue:等待的队列,当线程池中的线程数量大于corePoolSize的时候,就把该任务封装成Worker对象放入等待队列
  • keepAliveTime:如果线程池中的线程大于了核心线程数,如果没有继续提交任务,核心外的线程不会立即销毁,而是等到时间超过了keepAliveTime
  • TimeUnit:时间级别
  • threadFactory:用来创建新的线程
  • handler:表示线程池的饱和策略,如果阻塞队列满了并且没有空闲的线程,这时如果继续提交任务,就需要采取一种策略处理该任务
    • ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常(默认)
    • ThreadPoolExecutor.DiscardPolicy:丢弃任务,但是不抛出异常
    • ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新提交被拒绝的任务
    • ThreadPoolExecutor.CallerRunsPolicy:由调用线程(提交任务的线程)处理该任务

ScheduledThreadPoolExecutor

  • SingleScheduledThreadPool:创建单个定时线程池
  • ScheduledThreadPool:创建定时线程池

6、线程池的四种队列

ArrayBlockingQueue

特点:
数组支持的有界队列,如果初始化为1,不能扩容,只能等队列被消费了之后,才能继续添加,如果满了会将线程阻塞。

线程池任务提交原理

当正在执行的线程数等于corePoolSize时,多余的元素缓存在ArrayBlockingQueue队列中等待有空闲的线程时继续执行,当ArrayBlockingQueue已满时,加入ArrayBlockingQueue失败,会开启新的线程去执行,当线程数已经达到最大的maximumPoolSizes时,再有新的元素尝试加入ArrayBlockingQueue时会根据拒绝策略来进行处理。

LinkedBlockingQueue

链接节点支持的可选有界队列(也可以看成无界队列),当前执行的线程数量达到corePoolSize的数量时,剩余的元素会在阻塞队列里等待。maximumPoolSize会失效

PriorityBlockingQueue

优先级支持的无界优先级队列

DelayQueue

优先级堆支持的、基于时间的调度队列

SynchronousQueue

特点:
是一个不存储元素的阻塞队列,会直接将任务交给消费者,必须等队列中的添加元素被消费后才能继续添加新的元素

7、Executors的使用

newSingleThreadExecutor

只有一个可重复执行的线程

newFixedThreadPool

创建使用固定线程数的FixedThreadPool,适用于为了满足资源管理的需求,而需要限制当前线程数量的应用场景,它适用于负载比较重的服务器

  • corePoolSize:初始化的参数
  • workQueue:使用无界队列LinkedBlockingQueue链表阻塞队列(链表阻塞队列,使用put方法插入数据的时候如果满了,会一直等待空间,加入元素)
  • keepAliveTime = 0:因为使用了阻塞队列,任务超过了核心线程数,后续的任务都会添加到队列当中,这时maximunPoolSize就会失去意义

newCachedThreadPool

创建一个会根据需要创建新线程的,适用于执行很多的短期异步任务的小程序,或者是负载较轻的服务器。

  • keepAliveTime=60秒:没有核心线程数,设置空闲60秒,超过后就会销毁线程
  • workQueue=SynchronousQueue:不存储元素的阻塞队列必须等待一个take操作

newScheduledThreadPool

  • workQueue=delayWorkQueue:使用延迟队列作为缓存队列
  • schedule(Callable callable, long delay, TimeUnit unit)方法
  • callable:提交Callable或者Runnable任务
  • period:表示连个任务连续执行的时间周期,第一个任务开始到第二个任务的开始,包含了任务的执行时间,该方法在initialDelay时间后开始周期性的按period时间间隔执行任务
  • scheduleWithFixedDelay(Runnable command,long initialDelay,long delay,TimeUnit unit)方法

注意:创建的线程池实际上以不同的参数来创建的,创建出的线程池返回的都是ThreadPoolExecutor对象
注意:使用Executors创建线程池会出现内存溢出的问题,阿里巴巴规范指出,Executors创建线程池的默认最大容量是Integer.Max

问题归纳
1.FixedThreadPool

问题:并发量高的情况下容易出现线程池死锁的问题。

场景:父线程A(包括子线程1、子线程2)、父线程B(子线程3、子线程4)使用同一个固定线程池提交任务
故障:当线程池中全是父任务时,A和B。队列中存放的全是子线程,而子线程如果处理不结束,那么A和B线程也就不会结束,就会一直阻塞住,所以就会导致线程池死锁,并且因为FixedThreadPool采用的是无界队列,消息都会积压,队列就会越来越大

解决方案1:子任务在新的线程池中运行

解决方案2:采用有界队列,拒绝策略使用MQ来做降级处理

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值