首页 > 编程语言 > 详细

线程池

时间:2020-06-20 23:47:04      阅读:112      评论:0      收藏:0      [点我收藏+]

 使用线程池的原因

       多线程是为了用来最大化发挥多核处理器的处理能力,但是线程是不能无限创建的,当线程创建太多时,反而会消耗CPU与内存资源。

       线程的创建与销毁是需要时间的,假如一个线程的创建时间加上销毁时间还要远大于服务时间时,是得不偿失的;线程需要占用内存资源,大量线程的创建会占用宝贵内存资源,可以导致OOM (Out Of Memory)的问题;大量线程的回收会增加GC操作的压力增加GC的停顿时间;线程抢占CPU资源,会导致CPU在各个线程中进行上下文的切换,这也是一个耗时的过程。多线程的创建不仅是一个耗时,也占用资源的,因此就提出了线程池的解决方法。

线程池

       线程池是是事先创建若干个可执行的线程放入一个池(容器)中,有任务需要执行时就从池中获取空闲的线程,任务执行完成后将线程放回池,而不用关闭线程,可以减少创建和销毁线程对象的开销。避免了频繁的创建和销毁线程,让创建的线程达到复用的目的,线程池的内部维护一部分活跃的线程,如果有需要就从线程中取线程使用,用完归还到线程池,当然线程池对线程的数量是有一定的限制。线程池的本质是对线程资源的复用。

其优势是:

  • 降低资源的消耗:通过复用已经创建的线程降低线程创建和销毁的消耗
  • 提高响应速度:当任务到达时,任务直接从线程池中获取到一个线程就能立即执行
  • 提高线程的可管理性:线程资源的管理,提高系统的稳定性
  • 线程池可以进行统一的分配、调度和监控等功能

部分接口及实现类

Executor是最基础的执行的接口,ExecutorService接口继承了Excutor接口,提供了一些对线程池操作的扩展方法;AbstractExecutorService抽象类实现了ExecutorService接口提供的大部分方法;

ThreadPoolExecutor继承自AbstractExecutorService,部分方法的具体实现;

ScheduledExecutorService接口继承了ExecutorService接口,提供了”周期执行“的功能;

ScheduledThreadPoolExecutor既继承了TheadPoolExecutor线程池,也实现了ScheduledExecutorService接口,是带“周期执行“功能的线程池;

Executors是线程池的静态工厂,其提供了快捷创建线程池的静态方法。

ThreadPoolExecutor类

构造函数

 public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {...}

corePoolSize:核心的线程数。当我们提交一个任务时会创建一个新的线程执行,直到线程数等于corePoolSize,如果当前的线程数等于corePoolSize时,继续提交任务会被保存到阻塞队列中,等待被执行

maximumPoolSize:线程池允许的最大的线程的数量。

keepAliveTime:空闲线程的存活时间。默认情况下只有当线程数量大小corePoolSize时才有效。

unit:keepAliveTime的时间单位。

workQueue:等待队列,队列必须是BlockingQueue阻塞队列,被提交但尚未被执行的任务(线程数量大于corePoolSize)

排队的策略:

不排队,直接提交:SynchronoursQueue

无界队列:LinkedBlockingQueue

有界队列:ArrayBlockingQueue

threadFactory:线程工厂,用于创建线程。使用默认的线程工厂或者自定义线程工厂,自定义实现(实现ThreadFactory),要提供给线程创建时设置一个具有识别度的线程名(默认的线程工厂:DefaultThreadFactory 线程的命名规则:“Pool- 数字-thread-数字”)

 handler:线程池的饱和策略。由于任务过多或其他原因导致的线程池无法处理时的任务拒绝策略。

线程池在JDK内置的四种拒绝策略:

  • AbortPolicy:直接抛出异常,阻止线程继续执行。
  • CallerRunsPolicy:用调用者所在的线程来执行任务,如果被丢弃的任务未被关闭,则执行该线程任务,要注意的是CallerRunsPolicy的拒绝策略不会真的丢弃任务。
  • DiscardOldestPolicy:丢弃移除阻塞队列最靠前面(最早)的任务,并提交当前任务。
  • DiscardPolicy:丢弃当前的线程任务而不做任何处理。

除此以外,还可以自定义拒绝策略,实现RejectedExecutionHandler接口,捕获异常来实现自定义拒绝策略。

执行过程

如图

技术分享图片

  线程池在被创建时,只是向系统申请一个用于执行线程队列和管理线程池的线程资源。在调用execute()添加一个任务时,线程池才会进行一系列操作。

  1. 正在运行的线程数量小于corePoolSize时,线程池便会立刻创建新的线程来执行该线程任务,否则则在workQueue队列未满的情况下将该任务放入到阻塞队列中。
  2. 在阻塞队列已满(workQueue队列)并且正在运行的线程数小于miximumPoolSize时,线程池便会创建非核心线程立即执行该任务,否则线程池将拒绝执行该任务并抛出REjectExecutionException的异常。
  3. 在线程任务执行完毕后,该任务将会从线程池中的队列中移除,线程池将会从队列中取出下一个任务继续执行。
  4. 在线程处于空闲状态的时间超过keepAliveTime时间时,正在运行的线程数超过corePoolSize时,该线程将会被认为是空闲线程并停止。因此在线程池中的任务都被执行完毕后,线程池会缩小到corePoolSize大小。

 

线程池

原文:https://www.cnblogs.com/128-cdy/p/13170892.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!