1、线程池

线程池是一种 “池化” 的线程使用模式,通过创建一定数量的线程,让这些线程处于就绪状态来提高系统响应速度,在线程使用完成后归还到线程池来达到重复利用的目标,从而降低系统资源的消耗。

1.1、池的好处

使用线程池,有如下优势

  1. 降低资源消耗

    • 通过重复利用已创建的线程降低线程创建和销毁造成的消耗

  2. 提高响应速度

    • 当任务到达时,任务可以不需要等到线程创建就能立即执行

  3. 提高线程的可管理性

    • 线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控

  4. 提供更多更强大的功能

    • 线程池具备可拓展性,允许开发人员向其中增加更多的功能。比如延时定时线程池 ScheduledThreadPoolExecutor,就允许任务延期执行或定期执行

1.2、Executors

为了能更好的控制多线程,JDK 提供了一套 Executor 框架,其本质就是一个线程池,它的核心成员如下。

接口或类

说明

Executor 接口

定义了一个接收 Runnable 对象的方法 executor

ExecutorService 接口

一个比 Executor 使用更广泛的子类接口,其提供了生命周期管理的方法,以及可跟踪一个或多个异步任务执行状况返回 Future 的方法

AbstractExecutorService 抽象类

ExecutorService 执行方法的默认实现

ScheduledExecutorService 接口

一个可定时调度任务的接口

ScheduledThreadPoolExecutor类

ScheduledExecutorService 的实现,一个可定时调度任务的线程池

ThreadPoolExecutor 类

多用于创建线程池

常用方法

Executors 常用方法如下

  1. newCachedThreadPool()

    • 创建一个可缓存的线程池

    • CachedThreadPool 适用于并发执行大量短期耗时短的任务,或者负载较轻的服务器

  2. newFiexedThreadPool(int nThreads)

    • 创建固定数目线程的线程池

    • FiexedThreadPool 适用于负载略重但任务不是特别多的场景,为了合理利用资源需要限制线程数量的场景

  3. newSingleThreadExecutor()

    • 创建一个单线程化的 Executor

    • SingleThreadExecutor 适用于串行执行任务的场景,每个任务按顺序执行,不需要并发执行

  4. newScheduledThreadPool(int corePoolSize)

    • 创建一个支持定时及周期性的任务执行的线程池,多数情况下可用来替代 Timer

    • ScheduledThreadPool 是一个调度池,其实现了 schedulescheduleAtFixedRatescheduleWithFixedDelay 三个方法,可以实现延迟执行、周期执行等操作

  5. newSingleThreadScheduledExecutor()

    • 创建一个 corePoolSize 为 1 的 ScheduledThreadPoolExecutor

  6. newWorkStealingPool(int parallelism)

    • 返回一个 ForkJoinPool 实例

    • ForkJoinPool 主要用于实现 “分而治之” 的算法,适合于计算密集型的任务

避免使用Executors创建线程池

根据阿里《Java开发手册》,要避免使用 Executors 创建线程池,推荐使用 ThreadPoolExecutors 创建线程池。

  1. Executors 创建的 FiexedThreadPoolSingleThreadPool 任务队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM;

  2. Executors 创建的 CachedThreadPoolScheduledThreadPool 允许创建的线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM。

1.3、ThreadPoolExecutor

Java 中,线程池的实现类是 ThreadPoolExecutor,其构造函数如下。

public ThreadPoolExecutor(int corePoolSize,
                        int maximumPoolSize,
                        long keepAliveTime,
                        TimeUnit timeUnit,
                        BlockingQueue<Runnable> workQueue,
                        ThreadFactory threadFactory,
                        RejectedExecutionHandler handler)
  1. corePoolSize:线程池核心线程数

  2. maximumPoolSize:线程池所能容纳的最大线程数

  3. keepAliveTime:线程闲置存活时长

  4. timeUnit:线程闲置存活时长的时间单位,如 TimeUnit.MILLISECONDSTimeUnit.SECONDS

  5. blockingQueue:任务队列,常用的任务队列包括

    • ArrayBlockingQueue:一个数组实现的有界阻塞队列,此队列按照 FIFO 的原则对元素进行排序,支持公平访问队列

    • LinkedBlockingQueue:一个由链表结构组成的可选有界阻塞队列,如果不指定大小,则使用 Integer.MAX_VALUE 作为队列大小,按照 FIFO 的原则对元素进行排序

    • PriorityBlockingQueue :一个支持优先级排序的无界阻塞队列,默认情况下采用自然顺序排列,也可以指定 Comparator

    • DelayQueue:一个支持延时获取元素的无界阻塞队列,创建元素时可以指定多久以后才能从队列中获取当前元素,常用于缓存系统设计与定时任务调度等

    • SynchronousQueue:一个不存储元素的阻塞队列,存入操作必须等待获取操作,反之亦然

    • LinkedTransferQueue:一个由链表结构组成的无界阻塞队列,与 LinkedBlockingQueue 相比多了 transfertryTranfer 方法,该方法在有消费者等待接收元素时会立即将元素传递给消费者

    • LinkedBlockingDeque:一个由链表结构组成的双端阻塞队列,可以从队列的两端插入和删除元素

  6. threadFactory:线程工厂,用于指定为线程池创建新线程的方式,threadFactory 可以设置线程名称、线程组、优先级等参数,如通过 Google 工具包可以设置线程池里的线程名,代码如下。

newThreadFactoryBuilder().setNameFormat("general-detail-batch-%d").build();
  1. RejectedExecutionHandler:拒绝策略,常见的拒绝策略包括

    • ThreadPoolExecutor.AbortPolicy:默认策略,当任务队列满时抛出 RejectedExecutionException 异常

    • ThreadPoolExecutor.DiscardPolicy:丢弃掉不能执行的新任务,不抛任何异常

    • ThreadPoolExecutor.CallerRunsPolicy:当任务队列满时使用调用者的线程直接执行该任务

    • ThreadPoolExecutor.DiscardOldestPolicy:当任务队列满时丢弃阻塞队列头部的任务(即最老的任务),然后添加当前任务

todo...

文章作者:
本文链接:
版权声明: 本站所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 君の小站
编程 java maven thread
喜欢就支持一下吧