前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >线程池

线程池

作者头像
Java阿呆
发布2020-11-04 15:16:56
6310
发布2020-11-04 15:16:56
举报
文章被收录于专栏:Java阿呆Java阿呆Java阿呆

为什么要用线程池?

单线程方式存在以下几个问题:

  • 线程的工作周期:假设线程创建所需时间为T1,线程执行任务所需时间为T2,线程销毁所需的时间为T3,往往是T1+T3大于T2,所以如果频繁的创建线程会损耗过多的额外时间。
  • 如果有任务来了,再去创建线程的话效率比较低,如果从一个池子中可以直接获取可用的线程,那么效率会有所提升。所以线程池省去了任务过来要先创建线程的过程,节省了时间,提升了效率。
  • 线程池可以管理和控制线程,因为线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。
  • 线程池提供队列,存放缓冲等待执行的任务。

通过线程池创建线程从调用 API 角度来说分为两种,一种是原生的线程池,另外该一种是通过 Java 提供的并发包来创建,后者其实是对原生的线程池创建方式做了一次简化包装,让调用者使用起来更方便,但道理都是一样的。

ThreadPoolExecutor

通过ThreadPoolExecutor创建线程池,API如下所示:

ThreadPoolExecutor
ThreadPoolExecutor
  • corePoolSize:指定核心线程数(核心池大小)。在创建了线程池后,默认情况下,线程池中并没有任何线程,而是等待有任务到来才创建线程去执行任务,除非调用了prestartAllCoreThreads()或者prestartCoreThread()方法,预创建线程,即在任务到来之前就创建corePoolSize个或者一个线程。默认情况下,在创建了线程池后,线程池中的线程池数为0,当有任务来之后,就会创建一个线程去执行任务,当线程池中的线程数达到corePoolSize后,就会把到达的任务放到缓存队列中。
  • maximumPoolSize线程池最大线程数,它表示在线程池中最多能创建多少个线程。
  • keepAliveTime:表示线程没有任务执行时最多保持多久时间会终止。默认情况下,只有当线程池中的线程数大于 corePoolSize 时,keepAliveTime 才会起作用,直到线程池中的线程数不大于 corePoolSize,即当线程池中的线程数大于 corePoolSize 时,如果一个线程空闲的时间达到 keepAliveTime,则会终止,直到线程池中的线程数不超过 corePoolSize。但是如果调用了 allowCoreThreadTimeOut(boolean) 方法,在线程池中的线程数不大于 corePoolSize 时,keepAliveTime 参数也会起作用,直到线程池中的线程数为0。
  • unit:参数keepAliveTime的时间单位
  • workQueue:一个阻塞队列,用来存储等待执行的任务,这个参数会对线程池的运行过程产生重大影响,一般来说,这里的阻塞队列有以下几种选择:ArrayBlockingQueue、LinkedBlockingQueue、SynchronousQueue。
  • threadFactory线程工厂,用来创建线程。
  • handler:表示当拒绝处理服务时的策略(拒绝策略),有以下四种取值: ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectExecutionException异常。 ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。 ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)。 ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务。

线程池之间的参数协作如图所示(注意数字顺序):

线程池参数协作图
线程池参数协作图

1、任务优先向CorePool中提交,创建核心线程执行任务 2、在CorePool满了之后,任务被提交提交到任务队列,等待线程池空闲 3、在任务队列满了之后,但CorePool中还没有空闲线程,那么任务将被提交到maxPool中,创建非核心线程执行任务 4、msxPool满了之后执行task拒绝策略 具体流程图如下:

线程池流程图
线程池流程图

Executors

  Executor框架是一个根据一组执行策略调用、调度、执行和控制的异步任务的框架。   无限制的创建线程会引起应用程序内存溢出。所以创建一个线程池是个更好的的解决方案,因为可以限制线程的数量并且可以回收再利用这些线程。   利用Executors框架可以非常方便的创建一个线程池,Java通过Executors提供四种线程池,分别为:

  • newSingleThreadExecutor:创建一个线程的线程池,在这个线程池中始终只有一个线程存在。如果线程池中的线程因为异常问题退出,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。
  • newFixedThreadPool:创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。
  • newCachedThreadPool:可根据实际情况,调整线程数量的线程池,线程池中的线程数量不确定,如果有空闲线程会优先选择空闲线程,如果没有空闲线程并且此时有任务提交会创建新的线程。在正常开发中并不推荐这个线程池,因为在极端情况下,会因为 newCachedThreadPool 创建过多线程而耗尽 CPU 和内存资源。
  • newScheduledThreadPool:此线程池可以指定固定数量的线程来周期性的去执行。比如通过 scheduleAtFixedRate 或者 scheduleWithFixedDelay 来指定周期时间。

推荐使用ThreadPoolExecutor方式。   阿里的 Java 开发手册里有一条是不推荐使用 Executors 去创建线程池,而是推荐去使用 ThreadPoolExecutor 来创建。   这样做的主要原因是:使用 Executors 创建线程池不会传入核心参数,而是采用的默认值,这样的话我们往往会忽略掉里面参数的含义,如果业务场景要求比较苛刻的话,存在资源耗尽的风险;另外采用 ThreadPoolExecutor 的方式可以让我们更加清楚地了解线程池的运行规则,不管是面试还是对技术成长都有莫大的好处。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019-06-04,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 为什么要用线程池?
  • ThreadPoolExecutor
  • Executors
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档