什么是线程池

线程池是一种基于池化思想管理线程工具。

线程池的好处

  • 降低资源消耗: 通过重复利用已创建的线程来降低线程创建和销毁所造成的消耗。
  • 提高响应速度: 任务到达时可以立即执行,不需要等到线程创建再来执行任务。
  • 提高线程的可管理性: 线程是稀缺资源,如果无限制创建,不仅会消耗系统资源,还会因为线程的不合理分布导致资源调度失衡,降低系统的稳定性。使用线程池可以进行统一的分配、调优和监控。

怎么使用线程池

1. Executors 方式

Executors创建方式 一般不推荐使用,在阿里巴巴开发手册中也明确了这一点。

  • Executors.newFixedThreadPool:创建一个固定大小的线程池,可控制并发的线程数,超出的线程会在队列中等待;

  • Executors.newCachedThreadPool:创建一个可缓存的线程池,若线程数超过处理所需,缓存一段时间后会回收,若线程数不够,则新建线程;

  • Executors.newSingleThreadExecutor:创建单个线程数的线程池,它可以保证先进先出的执行顺序;

  • Executors.newScheduledThreadPool:创建一个可以执行延迟任务的线程池;

  • Executors.newSingleThreadScheduledExecutor:创建一个单线程的可以执行延迟任务的线程池;

  • Executors.newWorkStealingPool:创建一个抢占式执行的线程池(任务执行顺序不确定)【JDK 1.8 添加】。

2. ThreadPoolExecutor

最原始创建线程池的方式,它包含了7个参数可供设置。

public ThreadPoolExecutor(int corePoolSize, 
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.acc = System.getSecurityManager() == null ?
                null :
                AccessController.getContext();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

ThreadPoolExecutor参数介绍

corePoolSize

核心线程数(线程池的基本大小)当我们提交一个任务到线程池时就会创建一个线程来执行任务。当我们需要执行的任务数大于核心线程数了就不再创建, 如果我们调用了prestartAllCoreThreads()方法线程池就会为我们提前创建好所有的基本线程。

maximumPoolSize

最大线程数:线程池允许创建的最大线程数。如果队列已经满了,且已创建的线程数小于最大线程数,则线程池就会创建新的线程来执行任务。这里有个小知识点,如果我们的队列是用的无界队列,这个参数是不会起作用的,因为我们的任务会一直往队列中加,队列永远不会满(内存允许的情况)。

keepAliveTime

最大线程数可以存活的时间,当线程中没有任务执行时,最大线程就会销毁一部分,最终保持核心线程数量的线程。

unit

单位是和keepAliveTime 存活时间配合使用的,合在一起用于设定线程的存活时间 ,参数 keepAliveTime 的时间单位有以下 7 种可选:

  • TimeUnit.DAYS:天
  • TimeUnit.HOURS:小时
  • TimeUnit.MINUTES:分
  • TimeUnit.SECONDS:秒
  • TimeUnit.MILLISECONDS:毫秒
  • TimeUnit.MICROSECONDS:微妙
  • TimeUnit.NANOSECONDS:纳秒
workQueue

一个阻塞队列,用来存储线程池等待执行的任务,均为线程安全,它包含以下 7 种类型:

  • ArrayBlockingQueue:一个由数组结构组成的有界阻塞队列。此队列按照先进先出的原则对元素进行排序
  • LinkedBlockingQueue:一个由链表结构组成的有界阻塞队列。此队列按照先进先出的原则对元素进行排序,默认长度为Integer.MAX_VALUE
  • SynchronousQueue:一个不存储元素的阻塞队列,即直接提交给线程不保持它们。
  • PriorityBlockingQueue:一个支持优先级排序的无界阻塞队列。默认自然顺序排序,也可以定义实现CompareTo()方法指定元素排序规则
  • DelayQueue:一个使用优先级队列实现的无界阻塞队列,只有在延迟期满时才能从中提取元素。
  • LinkedTransferQueue:一个由链表结构组成的无界阻塞队列。与SynchronousQueue类似,还含有非阻塞方法。
  • LinkedBlockingDeque:一个由链表结构组成的双向阻塞队列
threadFactory

线程工厂,主要用来创建线程,默认为正常优先级、非守护线程。我们也可以通过实现ThreadFactory 来自定义线程工厂

handler

拒绝策略,拒绝处理任务时的策略,系统提供了 4 种可选:

  • AbortPolicy:拒绝并抛出异常。
  • CallerRunsPolicy:使用当前调用的线程来执行此任务。
  • DiscardOldestPolicy:抛弃队列头部(最旧)的一个任务,并执行当前任务。
  • DiscardPolicy:忽略并抛弃当前任务。

默认策略为 AbortPolicy

我们也可以自定义任务拒绝策略(实现下RejectedExecutionHandler接口),比如说如果任务拒绝了我们可以记录下日志,或者重试等,根据自己的业务需求来实现。

打赏
支付宝 微信
上一篇 下一篇