线程池相关

为什么用线程池?解释下线程池参数?
1、降低资源消耗;提高线程利用率,降低创建和销毁的消耗。
2.提高响应速度;任务来了,直接有线程可用可执行,而不是先创建线程,在执行。
3.提高线程的可管理性;线程是稀缺资源,使用线程池可以统一分配调优监控。

线程池的构造函数有7个参数,分别是corePoolSize、maximumPoolSize、keepAliveTime、unit、workQueue、threadFactory、handler。下面会对这7个参数一一解释。
一、corePoolSize 线程池核心线程大小
线程池中会维护一个最小的线程数量,即使这些线程处理空闲状态,他们也不会被销毁,除非设置了allowCoreThreadTimeOut。这里的最小线程数量即是corePoolSize。
二、maximumPoolSize 线程池最大线程数量
一个任务被提交到线程池以后,首先会找有没有空闲存活线程,如果有则直接将任务交给这个空闲线程来执行,如果没有则会缓存到工作队列(后面会介绍)中,如果工作队列满了,才会创建一个新线程,然后从工作队列的头部取出一个任务交由新线程来处理,而将刚提交的任务放入工作队列尾部。线程池不会无限制的去创建新线程,它会有一个最大线程数量的限制,这个数量即由maximunPoolSize指定。
三、keepAliveTime 空闲线程存活时间
一个线程如果处于空闲状态,并且当前的线程数量大于corePoolSize,那么在指定时间后,这个空闲线程会被销毁,这里的指定时间由keepAliveTime来设定
四、unit 空闲线程存活时间单位
keepAliveTime的计量单位
五、workQueue 工作队列
用来存放待执行的任务,假设现在核心线程都已被使用,还有任务进来则全部放入此工作队列中,任务调度时再从队列中取出任务,如果工作队列满了但还有任务进入,才会创建一个新线程。jdk中提供了四种工作队列:
①ArrayBlockingQueue
基于数组的有界阻塞队列,按FIFO排序。新任务进来后,会放到该队列的队尾,有界的数组可以防止资源耗尽问题。当线程池中线程数量达到corePoolSize后,再有新任务进来,则会将任务放入该队列的队尾,等待被调度。如果队列已经是满的,则创建一个新线程,如果线程数量已经达到maxPoolSize,则会执行拒绝策略。
②LinkedBlockingQuene
基于链表的无界阻塞队列(其实最大容量为Interger.MAX),按照FIFO排序。由于该队列的近似无界性,当线程池中线程数量达到corePoolSize后,再有新任务进来,会一直存入该队列,而不会去创建新线程直到maxPoolSize,因此使用该工作队列时,参数maxPoolSize其实是不起作用的。
③SynchronousQuene
一个不缓存任务的阻塞队列,生产者放入一个任务必须等到消费者取出这个任务。也就是说新任务进来时,不会缓存,而是直接被调度执行该任务,如果没有可用线程,则创建新线程,如果线程数量达到maxPoolSize,则执行拒绝策略。
④PriorityBlockingQueue
具有优先级的无界阻塞队列,优先级通过参数Comparator实现。
六、threadFactory 线程工厂
用于设置创建线程的工厂。我们可以选择使用默认的创建工厂(产生的线程都在同一个组内,拥有相同的优先级,且都不是守护线程),也可以选择自定义线程工厂(可以用来设定线程名、优先级、是否为daemon线程等等)
七、handler 拒绝策略
当工作队列中的任务已到达最大限制,并且线程池中的线程数量也达到最大限制,这时如果有新任务提交进来,该如何处理呢。这里的拒绝策略,就是解决这个问题的,这个策略默认情况下是AbortPolicy,表示无法处理新任务时抛出异常,jdk中提供了4中拒绝策略:
AbortPolicy:直接抛出异常RejectedExecutionException。
CallerRunsPolicy:只用调用者所在线程来运行任务,除非线程池已经shutdown,则直接抛弃任务。
DiscardOldestPolicy:丢弃队列里最早的一个任务,然后尝试把这次拒绝的任务放入队列
DiscardPolicy:不处理,丢弃掉。

线程池处理流程
1、提交任务后,线程池判断线程数是否达到了核心线程数,如果运行的线程少于corePoolSize,则创建核心线程处理任务,否则就执行下一步。
2、线程池判断任务队列是否满了。如果没满,则将任务添加到任务队列中,否则执行下一步
3、线程池判断线程数是否达到了最大线程数。如果未达到,则创建临时(非核心)线程处理任务,否则执行拒绝策略。

线程中阻塞队列的作用?为什么是先添加队列而不是先创建最大线程?
1、一般的队列只能保证作为一个有限长度的缓冲区,如果超过了缓冲长度,就无法保留当前的任务了,阻塞队列通过阻塞可以保留住当前想要继续入列的任务。
阻塞队列可以保证任务队列中没有任务时阻塞获取任务的线程,使得线程进入wait状态,释放cpu资源
阻塞队列自带阻塞和唤醒功能,不需要额外处理,无任务执行时,线程池利用阻塞队列的take方法挂起,从而维持核心线程的存活,不至于一致占用cpu资源。
阻塞队列主要是用于生产者-消费者模型的情况。
比如一个线程从一个空的阻塞队列中取元素,此时线程会被阻塞直到阻塞队列中有了元素。当队列中有元素后,被阻塞的线程会自动被唤醒(不需要我们编写代码去唤醒)。这样提供了极大的方便性。
如果使用非阻塞队列,它不会对当前线程产生阻塞,就必须额外地实现同步策略以及线程间唤醒策略,这个实现起来就非常麻烦。
2、 在创建新线程的时候,是要获取全局锁的,这个时候其他的就需要阻塞,影响了整体效率。就好比一个企业里面有十个(core)正式工的名额,最多招十个正式工(核心线程),要是任务超过正式人数(task>core)的情况下,工厂领导(线程池)不是首先扩招工人,还是这十个人,但是任务可以稍积压一下。即先放到队列中去(代价低)。十个正式工慢慢干,迟早会干完的,如果任务还在持续增加,超过正式工的加班忍耐极限了(队列满了),就招外包(非核心线程)帮忙了,还是正式工加外包还不能完成任务,那么新来的任务就会被领导拒绝(线程池拒绝策略)。

线程池主要是为了避免频繁的创建和销毁线程,如果当核心线程数满了时,新任务进入,先创建线程,新任务完成后,又要销毁线程,这样又是频繁的创建和销毁线程,非常耗性能。而先添加至队列,这样代价比较小。
最大线程数主要是为了业务的峰值考虑。

线程池中线程复用原理
线程池将线程和任务进行解耦,线程是线程,任务是任务,摆脱了之前通过 Thread 创建线程时的一个线程必须对应一个任务的限制。在线程池中,同一个线程可以从阻塞队列中不断获取新任务来执行,其核心原理在于线程池对Thread进行了封装,并不是每次执行任务都会调用Thread.start()来创建新线程,而是让每个线程去执行一个“循环任务”,在这个“循环任务”中不停检查是否有任务需要被执行,如果有则直接执行,也就是调用任务中的run方法,将run方法当成一个普通的方法执行,通过这种方式只使用固定的线程就将所有任务的 run 方法串联起来

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值