04 线程池

1. 为什么要用线程池

创建线程和回收线程都会占用系统资源,如果任务来了才创建线程那么响应时间会变长。因此,需要提前创建一些线程,这些线程交给线程池管理。这样可以降低资源消耗,提高响应速度,提高线程的可管理性。

2. 线程池参数的意义
  • corePoolSize:表示核心线程池的大小。当提交一个任务时,如果当前线程池的线程个数没有达到corePoolSize,则会创建新的线程来执行所提交的任务,即使当前核心线程池有空闲的线程。如果当前核心线程池的线程个数已经达到了corePoolSize,则不再重新创建线程。如果调用了prestartCoreThread()或者 prestartAllCoreThreads(),线程池创建的时候所有的核心线程都会被创建并且启动。
  • maximumPoolSize:表示线程池能创建线程的最大个数。如果当阻塞队列已满时,并且当前线程池线程个数没有超过maximumPoolSize的话,就会创建新的线程来执行任务。
  • keepAliveTime:空闲线程存活时间。如果当前线程池的线程个数已经超过了corePoolSize,并且线程空闲时间超过了keepAliveTime的话,就会将这些空闲线程销毁,这样可以尽可能降低系统资源消耗。但是如果调用了allowCoreThreadTimeOut(boolean)方法,在线程池中的线程数不大于corePoolSize时,keepAliveTime参数也会起作用,直到线程池中的线程数为0;
  • unit:参数keepAliveTime的时间单位
  • workQueue:一个阻塞队列,用来存储等待执行的任务。这个参数的选择也很重要,会对线程池的运行过程产生重大影响。
  • threadFactory:用来创建线程的线程工厂
  • handler:饱和策略(拒绝策略)。当线程池的阻塞队列已满并且线程数量达到了最大线程数,就需要采用拒绝策略来处理这种情况。
3. 从0开始一直往线程池扔任务(无限个),讲讲这中间发生的过程(线程池原理)
  1. 提交任务首先判断是否是null,如果是null就抛出空指针异常,如果不是就执行下一步。
  2. 如果当前运行的线程少于corePoolSize,则创建新线程来执行任务(要获取全局锁)。
  3. 如果运行的线程数大于等于corePoolSize,则将任务加入BlockingQueue。
  4. 如果BlockingQueue满了的话,无法将任务加入。就需要创建新的线程来处理任务。
  5. 如果创建新线程会使当前运行的线程超过maximumPoolSize,任务将会被拒绝。
4. 最大线程数在什么时候会生效,会触发。

阻塞队列满了的时候,如果使用的无界的队列,这个参数就失效了。

5. 任务队列满了以后再来一个任务如何处理?

判断线程池运行的线程数量是否达到了最大线程数量。如果没有,则创建一个新的工作线程来执行任务。如果已经满了,执行拒绝策略。

6. 线程池拒绝策略有哪些?
  • AbortPolicy丢弃,抛出异常
  • DiscardPolicy直接丢弃
  • DiscardOldestPolicy丢弃队列里最近的一个任务,并执行当前任务
  • CallerRunsPolicy只用调用者所在线程来运行任务
    也可以自定义策略,需要实现RejectedExecutionHandler接口
7. 讲一下线程池有哪些(常见的线程池,线程池的种类,线程池的实现类)
  • ①newSingleThreadExecutor()
    单个线程的线程池,即线程池中每次只有一个线程工作,单线程串行执行任务
  • ②newFixedThreadPool(int n)
    固定数量的线程池,每提交一个任务就是一个线程,直到达到线程池的最大数量,然后后面进入等待队列直到前面的任务完成才继续执行
  • ③newCacheThreadPool(推荐使用)
    可根据实际情况调整线程数量的线程池,当线程池大小超过了处理任务所需的线程,那么就会回收部分空闲(一般是60秒无执行)的线程,当有任务来时,又智能的添加新线程来执行。
  • ④newScheduleThreadPool(int corePoolSize)
    大小无限制的线程池,支持定时和周期性的执行线程
8. 线程池中一般设置多少线程?
  • 根据任务性质来决定,任务一般可分为:CPU密集型、IO密集型、混合型,对于不同类型的任务需要分配不同大小的线程池。
  • CPU密集型任务 尽量使用较小的线程池,一般为CPU核心数+1。 因为CPU密集型任务使得CPU使用率很高,若开过多的线程数,只能增加上下文切换的次数,因此会带来额外的开销。
  • IO密集型任务 可以使用稍大的线程池,一般为2*CPU核心数。 IO密集型任务CPU使用率并不高,因此可以让CPU在等待IO的时候去处理别的任务,充分利用CPU时间。
  • 混合型任务可以将任务分成IO密集型和CPU密集型任务,然后分别用不同的线程池去处理。 只要分完之后两个任务的执行时间相差不大,那么就会比串行执行来的高效。因为如果划分之后两个任务执行时间相差甚远,那么就没有必要分解。
9. 线程中中常用阻塞队列有哪些,你一般用哪个,LinkedBlockingQueue与ArrayBlockingQueue的优缺点对比
  • ArrayBlockingQueue基于数组的阻塞队列,有界队列,按照先进先出(FIFO)的形式,初始化是必须指定capacity。
  • LinkedBlockingQueue基于链表的阻塞队列,元素按照先进先出(FIFO)的策略。
  • SynchronousBlockingQueue容量为0,不存储任务,通俗地讲就是有一个处理一个。
  • PriorityBlockingQueue这是一个无界有序的阻塞队列,排序规则和之前介绍的PriorityQueue一致,只是增加了阻塞操作。同样的该队列不支持插入null元素,同时不支持插入非comparable的对象。
  • LinkedBlockingQueue和ArrayBlockingQueue区别:ArrayBlockingQueue在put,take操作使用了同一个锁,两者操作不能同时进行,而LinkedBlockingQueue使用了不同的锁,put操作和take操作可同时进行,以此来提高整个队列的并发性能。

详细的 LinkedBlockingQueue内部由单链表实现,只能从head取元素,从tail添加元素。添加元素和获取元素都有独立的锁,是读写分离的,读写操作可以并行执行。LinkedBlockingQueue采用可重入锁(ReentrantLock)来保证在并发情况下的线程安全。LinkedBlockingQueue默认容量为Integer.MAX,添加元素的方法有put()、offer(),取元素的操作有take()、poll()

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值