我们在学线程的时候了解了几种创建线程的方式,比如继承Thread类,实现Runnable接口、Callable接口等,那对于线程池的使用,也需要去创建它,在这里我们提供2种构造线程池的方法:

方法一: 通过ThreadPoolExecutor构造函数来创建(首选)
   这是JDK中最核心的线程池工具类,在JDK1.8中,它提供了丰富的可设置的线程池构造参数,供我们设计不同的线程池,如下:
image

通过构造方法 ,可以给整个线程池设置大小、等待队列、非核心线程存活时间、创建线程的工厂类、拒绝策略等,具体参数描述可见 第六问,它们在线程池中所对应的关系,可见下图。

image

方法二: 通过 Executor 框架的工具类 Executors 来创建(不推荐)
   Executors 是java并发工具包中的一个静态工厂类,在JDK1.5时被创造出来,提供了丰富的创造线程池的方法,通过它可以创建多种类型的线程池。

image

  • newFixedThreadPool:创建定长线程池,该线程池中的线程数量始终不变。当有一个新的任务提交时,线程池中若有空闲线程,则立即执行。若没有,则新的任务会被暂存在一个任务队列中,待有线程空闲时,便处理在任务队列中的任务。当线程发生错误结束时,线程池会补充一个新的线程;
  • newCachedThreadPool:创建可缓存的线程池,如果线程池的容量超过了任务数,自动回收空闲线程,任务增加时可以自动添加新线程,所有线程在当前任务执行完毕后,将返回线程池进行复用,线程池的容量不限制;
  • newScheduledThreadPool:创建定长线程池,可执行周期性的任务;
  • newSingleThreadExecutor:创建单线程的线程池,只有一个线程的线程池。若多余一个任务被提交到该线程池,任务会被保存在一个任务队列中,待线程空闲,按先入先出的顺序执行队列中的任务,线程异常结束,会创建一个新的线程,能确保任务按提交顺序执行;
  • newWorkStealingPool:任务可窃取线程池,不保证执行顺序,当有空闲线程时会从其他任务队列窃取任务执行,适合任务耗时差异较大的场景。

为何很多大厂都禁止使用Executors 创建线程池呢?

   如果大家跟入到Executors这些方法的底层实现中去看一眼的话,立马就知道原因了,像FixedThreadPool 和 SingleThreadExecutor这两个方法内使用的是无界的 LinkedBlockingQueue存储任务,任务队列最大长度为 Integer.MAX_VALUE,这样可能会堆积大量的请求,从而导致 OOM。

   而CachedThreadPool使用的是同步队列 SynchronousQueue, 允许创建的线程数量也为 Integer.MAX_VALUE ,如果任务数量过多且执行速度较慢,可能会创建大量的线程,从而导致 OOM,其他的方法所提供的均是这种无界任务队列,在高并发场景下导致OOM的风险很大,故大部分的公司已经不建议采用Executors提供的方法创建线程池了。

posted on 2024-06-06 08:29  JavaBuild  阅读(496)  评论(1编辑  收藏  举报