聊聊线程池以及面试中:如何设计一个优先级线程池?

前言:本篇解决两个问题,浅聊线程池以及美团二面的一个问题:你怎么去设计一个优先级线程池

一、浅聊线程池

        首先提到线程池你会想到什么?线程、池子,没错,类比于对象池,常量池,连接池一样。

        线程池(Thread Pool)是一种管理线程的技术,可以避免线程的频繁创建以及销毁这一过程,它把线程丢到队列中进行统一的管理。线程池本身会有一些核心参数来控制线程,在JDK8中源码是这样描述的:

  • 这个类提供线程局部变量。 这些变量与其正常的对应方式不同,因为访问一个的每个线程(通过其getset方法)都有自己独立初始化的变量副本。 ThreadLocal实例通常是希望将状态与线程关联的类中的私有静态字段(例如,用户ID或事务ID)。
  •  public class ThreadId {
         // Atomic integer containing the next thread ID to be assigned
         private static final AtomicInteger nextId = new AtomicInteger(0);
    
         // Thread local variable containing each thread's ID
         private static final ThreadLocal<Integer> threadId =
             new ThreadLocal<Integer>() {
                 @Override protected Integer initialValue() {
                     return nextId.getAndIncrement();
             }
         };
    
         // Returns the current thread's unique ID, assigning it if necessary
         public static int get() {
             return threadId.get();
         }
     } 

        接下来介绍一下线程池的核心参数:

  • corePoolSize:核心线程的大小,线程池一直在运行,核心线程就不会停止
  • maximumPoolSize:线程池最大线程数。同上,如果maximumPoolSize是10,corePoolSize是5的话,那么非核心线程就是10-5=5。
  • keepAliveTime:非核心线程的心跳时间。如果非核心线程没有心跳,那么就会消亡
  • workQueue:阻塞队列。在下面第二问中会详细解读
  • defaultHandler:饱和策略,在ThreadLocalPool中一共有四种饱和策略。实现RejectedExecutionHandler接口:
  1. AbortPolicy:默认饱和策略,线程任务丢弃报错。
  2. DiscardPolicy:线程任务直接丢弃不报错。
  3. DiscardOldestPolicy:丢弃workQueue队列首任务丢弃,添加最新的任务加入到队列中执行
  4. CallerRunsPolicy:线程池之外的线程直接调用run方法执行

二、如何设计一个优先级线程池

        需要注意的是,在阿里巴巴Java开发手册中写到:“线程池不允许使用 Executors 去创建,而是通过ThreadPoolExecutor的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。”

    Executors 里面默认提供的几个线程池是有一些弊端的,如果是不懂多线程、或者是新手直接盲目使用,就可能会造成比较严重的生产事故。  

        第一个、无界队列:FixedThreadPool 和 SingleThreadPool 中,阻塞队列长度(线程池的容量)是 Integer.Max_Value,一旦请求量增加,就会堆积大量请求阻塞在队列中,可能会造成内存溢出的问题。FixedThreadPool只能创建核心线程数的线程、SingleThreadPool只能创建一个线程

        第二个,同步队列:CachedThreadPool 和 ScheduledThreadPool没有容量,不存储元素,如果有空闲线程,则使用空闲线程来处理,否则就创建一个新线程。CachedThreadPool 中最大线程数量是 Integer.Max_value,一旦请求量增加,导致创建大量的线程,使得处理性能下降。 甚至可能会出现宕机的问题(OOM)。

        第三个,延迟阻塞队列:ScheduledThreadPool和SingleThreadScheduledExecutor。内部采用堆的数据结构,可以保证每次出队的任务都是当前队列中执行时间最靠前的,在DelayedWorkQueue中元素添加满之后会自动扩容一半,也就是永远不会阻塞,最大可以达到Integer.Max_Value

        优先级任务线程池的实现:使用PriorityBlockingQueue作为任务队列,实例化一个ThreadLocalExecutor对象,在workQueue参数中传入,可以把它看做一个线程安全的PriorityQueue,底层使用的是小顶堆形式的二叉堆,最小值元素优先出列。但是它不支持阻塞操作。要是让PriorityBlockingQueue实现排序有两种操作:

1、提交到线程池的任务实现Comparable接口,并且重写compareTo方法来指定任务之间优先级比较规则

2(推荐)、创建PriorityBlockingQueue的时候传入Comparator对象指定任务间的规则

当然,可能会存在一些问题,比如PriorityBlockingQueue是无界的,如果堆积大量的请求会导致OOM,其次,如果分配不得当的话可能会导致低优先级的任务长时间无法被执行,而且要抱枕线程安全采用的是ReentrantLock可重入锁,会降低性能。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值