关于线程池其他一些知识点的讨论


前言

这篇博客记录一下关于线程池的几种任务队列,一般常用的任务队列是哪种,以及怎么确定线程池的线程数,还有几种常用的线程池创建方式。


一、线程池的几种任务队列

常用的就是ArrayBlockingQueue和LinkedBlockingQueue

1.ArrayBlockingQueue

  • ArrayBlockingQueue是一种有界的数组型任务队列,基于FIFO(先进先出的任务队列)

在这里插入图片描述
如图看ArrayBlockingQueue的构造函数,参数中有一个容量的参数是必传的,因此它是一个有界的任务队列。

2.LinkedBlockingQueue

  • LinkedBlockingQueue是一个无解的底层是一个单链表的任务队列,也是基于FIFO(先进先出的任务队列)

在这里插入图片描述
它默认是无界的,是Integer的最大值。

3.DelayedWorkQueue

  • 是一个优先级队列,它可以保证每次出队的任务都是当前队列中执行时间最靠前的

4.SynchronousQueue

  • 不存储元素的阻塞队列,每个插入操作都必须等待下一个移出操作

二、如何确定核心线程数?

  • 1.高并发、任务执行时间短,采用(CPU核数+1)原则确定核心线程数,减少线程上下文的切换
  • 2.如果是并发不高、任务执行时间长,若是IO密集型任务(CPU核数*2 + 1);若是计算密集型任务(CPU核数+1)
  • 3.并发高、且业务执行时间长,解决这种类型任务的关键不在于线程池而在于整体架构设计,看看这些业务里面某些数据是否能做缓存是第一步,增加服务器是第二步,至于线程池的设置,参考以上原则进行设置

三、线程池的种类有哪些?

1.newFixedThreadPool

  • 创建一个定长的线程池,可控制最大并发数,超出的线程会在队列中等待

在这里插入图片描述
在这里插入图片描述
看源码可以知道,传入的线程数就是最大线程数和核心线程数,救急线程的活跃时间是0毫秒,使用的任务队列是LinkedBlockingQueue,而且并没有传入任务队列的容量,意味着容量是Integer.MAX_VALUE,这是一个风险

2.newSingleThreadExecutor

  • 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO)执行

在这里插入图片描述
在这里插入图片描述
newSingleThreadExecutor的核心线程数和最大线程数都是1,救急线程的存活时间为0毫秒,任务队列是LinkedBlockingQueue

3.newCacheThreadPool

  • 创建一个可缓存的线程池,如果线程池长度超过,处理需要,可灵活回收空闲线程,若无可回收,则新建线程

在这里插入图片描述
在这里插入图片描述
newCacheThreadPool核心线程数为0,最大线程数为Integer.MAX_VALUE,救急线程存储时间为60S,任务队列是SynchronousQueue,不存储任务的队列

4.newScheduledThreadPool

  • 可以执行延迟任务的线程池,支持定时及周期性任务执行

在这里插入图片描述
newScheduledThreadPool核心线程数需要定义,最大线程数是Integer.MAX_VALUE,救急线程存活时间是0ms,任务队列是DelayedWorkQueue

四、为什么不建议使用Executor创建线程池?

  • 1.线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式,这样的处理方式让写的人更加明确线程池的运行规则,避免资源耗尽的风险
  • 2.FixedThreadPool和SingleThreadPool:允许的请求队列长度为Integer.MAX_VALUE,可能会堆积大量的请求,从而导致OOM(内存溢出)
  • 3.CachedThreadPool:允许的创建线程数量为Integer.MAX_VALUE,可能会创建大量的线程,从而导致OOM

总结

线程池有关知识点先告一段落,下面我会继续总结并发编程中的安全相关问题,比如Synchronized锁,乐观锁,悲观锁,以及死锁等问题。

猜你喜欢

转载自blog.csdn.net/qq_40187702/article/details/131627465